Tutorial: Adding new functionality to RepTate¶
Contents
Goals¶
In this section, we will present how to create a new RepTate application. We will show the important steps needed to build a new RepTate application. To do so, we will need to modify some already existing RepTate code, as well as writing new custom code.
As an example, let us say that we want to create a new RepTate application that:
accepts text files (
.xy
) which are supposed to be composed of:a first line (header) containing the
date
and temperatureT
two columns containing data \(x\) and \(y\)
allows to view the data:
without transformation, \(x\) vs \(y\)
\(x\) vs \(\sqrt{y}\)
has a theory: a simple line fitting with two parameters \(a\) and \(b\) such that \(y = ax+b\).
Tip
Each time we need to modify or add some line of Python code, the line number is indicated
on the left hand side for information.
This should correspond to the same line numbering for the “template” files, but might
be different for the files in the core/
or gui/
folders.
New application¶
Create a new application file¶
To create a new RepTate application, we will use the template
application file ApplicationTemplate.py
that can be found in the
applications/
folder.
Make a copy of this file and rename it with a sensible name that relates to the application purpose. According to the Goals section, we name it
ApplicationXY.py
.Open
ApplicationXY.py
with your favourite text editor and replace all the occurrences of “Template” by “XY”. For example,46 class ApplicationTemplate(CmdBase):
becomes
46 class ApplicationXY(CmdBase):
Give a brief description of the purpose of the application, e.g. “Application reading XY files”. The first lines of
ApplicationXY.py
should now look like33"""Module ApplicationXY 34 35Application reading XY files 36 37"""
The file ApplicationXY.py
is ready for the next round of modifications
that are (i) file types accepted by the application, (ii) the views,
and (iii) the theories, as defined in the Goals section.
But first, we have to let RepTate know about our new application.
Edit RepTate’s ApplicationManager
¶
We need to add a reference to this new application into
RepTate’s ApplicationManager
, so it knows it exists. To do so:
Insert this line in the top part of the file
core/ApplicationManager.py
, e.g.56from ApplicationXY import ApplicationXY
Insert the following line to add an entry to the
ApplicationManager
dictionary115self.available_applications[ApplicationXY.appname] = ApplicationXY
Note
Our new application is ready to be used in Command Line RepTate!
Edit RepTate’s QApplicationManager
¶
In order to have our new application available in the Graphical
User Interface (GUI) version of RepTate (and not just available in the
Command-Line version of RepTate), we need to create a new “button”
that will launch our new application when clicked.
We will edit the file gui/QApplicationManager.py
in this purpose.
Add a button in the main RepTate tool-bar by inserting the following lines in the
__init__
method ofgui/QApplicationManager.py
. The icon name (filename) should correspond to theappmane
, hereXY.png
. See the section New Icons to create and use your onwn icon in RepTate.142 # ApplicationXY button 143 #choose the button icon 144 icon = QIcon(':/Icon8/Images/new_icons/XY.png') 145 tool_tip = 'XY' # text that appear on hover 146 self.actionXY = QAction(icon, tool_tip, self) 147 #insert the new button before the "MWD" button 148 self.toolBarApps.insertAction(self.actionMWD, self.actionXY)
The new button has been successfully inserted into the application tool bar. However, if we click on it, nothing happens because it is not linked to any action. We need to “wire” (connect) this new button to a “function”. In the same file
gui/QApplicationManager.py
, below the previous lines, add149 #connect button 150 self.actionXY.triggered.connect(lambda: self.handle_new_app('XY'))
Warning
The application name (
appname
), defined at line 79 ofApplicationXY.py
, should then be “XY”. Additionally, the icon name defining the logo of the new application should be named “XY.png”, see the definition of thehandle_new_app
method.
Note
Our new application is ready to be used in GUI RepTate!
Note on default theories¶
By default, some “basic theories” are included with the application
(e.g. polynomial, power-law, exponential). To remove all these
“basic theories” from your new application, comment the following line
in the __init__
method of class BaseApplicationXY
135self.add_common_theories() # Add basic theories to the application
New file type¶
RepTate applications are designed to accept a only a
predefined file extension. As defined in the Goals section,
we want our new application ApplicationXY.py
to accept .xy
files.
To do so, we modify ApplicationXY.py
as follows.
In class ApplicationXY
, before def __new__
, add
53extension = "xy" # drag and drop this extension automatically opens this application
In the __init__
method of class BaseApplicationXY
add
120# set the type of files that ApplicationTemplate can open
121ftype = TXTColumnFile(
122 name='XY data', # name the type of data
123 extension='xy', # file extension
124 description='XY data from XY-experiment',
125 col_names=['X', 'Y'], # name the variables for legend
126 basic_file_parameters=['date', 'T'], # parameter in file header
127 col_units=['-', '-']) # units of X and Y (here none)
New view¶
About the “old” view¶
At the moment, only one view is allowed in our new ApplicationXY
.
That view is located in the __init__
method of
class BaseApplicationXY
:
95# VIEWS
96# set the views that can be selected in the view combobox
97self.views['y(x)'] = View(
98 name='y(x)',
99 description='y as a function of x',
100 x_label='x',
101 y_label='y(x)',
102 x_units='-',
103 y_units='-',
104 log_x=False,
105 log_y=False,
106 view_proc=self.viewyx,
107 n=1,
108 snames=['y(x)'])
The important attributes of the view called “y(x)” are:
the x- and y-label to be used in the plot,
the units that are appended to the x- and y-labels,
the
log_x
andlog_y
define whether the axes should be in in log-scale (base 10)self.viewyx
is the method that defines what operations are done on the data before plotting them (see below),n
defines the number of series the view is plotting.
In the line below, you can define the default number of view, i.e., the number of views that appear when you open the appliction. In case the new application would benefit from having multiple views shown at the same time (similar to the React or Stress Relaxation applications), this number can be increased (up to 4)
110# set multiviews
111# default view order in multiplot views, set nplots=1 for single view
112self.nplots = 1
The definition of the method viewyx
is
given by
140def viewyx(self, dt, file_parameters):
141 """Documentation"""
142 x = np.zeros((dt.num_rows, 1))
143 y = np.zeros((dt.num_rows, 1))
144 x[:, 0] = dt.data[:, 0]
145 y[:, 0] = dt.data[:, 1]
146 return x, y, True
The two lines x[:, 0] = dt.data[:, 0]
and y[:, 0] = dt.data[:, 1]
tell us that viewyx
does not perform any operations on the data.
It simply copies the input data into x
and y
arrays. It means that
we already have one of the views required from the Goals section.
Definition of a new view¶
To define a new view that shows \(x\) vs \(\sqrt{y}\), as
requested in the Goals section, we add a view to
self.views
dictionary. The new view is called “sqrt(y)”.
In the __init__
method of class BaseApplicationXY
, add
110self.views['sqrt(y)'] = View(
111 name='sqrt(y)',
112 description='sqrt(y) as a function of x',
113 x_label='x',
114 y_label='$y^{1/2}$',
115 x_units='-',
116 y_units='-',
117 log_x=False,
118 log_y=False,
119 view_proc=self.view_sqrt_y,
120 n=1,
121 snames=['sqrt(y)'])
Tip
The x_label
and y_label
support LaTeX-like syntax.
We also need to define the new method view_sqrt_y
.
In class BaseApplicationXY
, add the definition
158def view_sqrt_y(self, dt, file_parameters):
159 """Documentation"""
160 x = np.zeros((dt.num_rows, 1))
161 y = np.zeros((dt.num_rows, 1))
162 x[:, 0] = dt.data[:, 0]
163 y[:, 0] = (dt.data[:, 1])**0.5
164 return x, y, True
Note
The new view is ready!
New theory¶
Create a new theory file¶
To create a new RepTate application, we will use the template
theory file TheoryTemplate.py
that can be found in RepTate
theories/
folder.
Make a copy of this file and rename it with a sensible name that relates to the theory purpose. According to the Goals section, we name it
TheoryLine.py
.Open
TheoryLine.py
with your favourite text editor and replace all the occurrences of “Template” by “Line”. For example,45 class TheoryTemplate(CmdBase):
becomes
45 class TheoryLine(CmdBase):
Give a brief description of the purpose of the application, e.g. ” Theory fitting a line to the data”. The first lines of
TheoryLine.py
should now look like33"""Module TheoryLine 34 35Theory fitting a line to the data 36 37""" 38import numpy as np
The file TheoryLine.py
is ready for the next round of modifications
that are (i) define the parameters, (ii) define the theory “function”.
But first, we have to let ApplicationXY (developed just above) know about
our new theory.
Edit ApplicationXY.py
¶
We need to add a reference to this new theory into
ApplicationXY.py
, so it knows it exists. To do so:
Insert the following line in the
__init__
method ofclass BaseApplicationXY
, after the “# IMPORT THEORIES
” comment89 # IMPORT THEORIES 90 # Import theories specific to the Application e.g.: 91 from TheoryLine import TheoryLine
Hint
We choose to place the theories
import
inside the__init__
method ofclass BaseApplicationXY
rather than in the very top of the fileApplicationXY.py
as this prevents RepTate from loading all theories at start. Instead, theories are loaded only when an application using them is opened.Insert the following line, also in the
__init__
method ofclass BaseApplicationXY
, after the# THEORIES
, and beforeself.add_common_theories()
, the line134 self.theories[TheoryLine.thname] = TheoryLine
Edit the theory¶
According to the Goals section, the theory should define a straight line \(y=ax+b\), hence there are two parameters. We will (i) write a short documentation of our new theory, (ii) define the parameters, and (iii) write the main function that calculates the theory values.
Add a Python docstring to (auto)-document the theory. Place some description of the goal of the theory as well as a description of the parameters. This will help future reader of the file understand the purpose of the theory and it will be automatically integrated to the online RepTate documentation (reptate.readthedocs).
46 class TheoryLine(CmdBase): 47 """Fit a straight line. 48 49 * **Function** 50 .. math:: 51 y = a x + b 52 53 * **Parameters** 54 - :math:`a`: slope of the line 55 - :math:`b`: the :math:`y`:-intercept 56 57 """
To define the theory parameters, \(a\) and \(b\), we modify the
__init__
method ofclass BaseTheoryLine
to have only these two parameter definitions95 self.parameters['a'] = Parameter( 96 name='a', 97 value=1, 98 description='parameter a', 99 type=ParameterType.real, 100 opt_type=OptType.opt) 101 self.parameters['b'] = Parameter( 102 name='b', 103 value=0, 104 description='parameter b', 105 type=ParameterType.real, 106 opt_type=OptType.opt)
The important attributes of the parameters are:
value
: the initial value of the parametertype
: defines if he parameter is real, integer or discreteopt_type
: optimisation type is eitherconst
for constant parameter (cannot be optimised),opt
if the parameter is optimised by default,nopt
if the parameter can be optimised but is not by default.
Modify the method
calculate
ofclass BaseTheoryLine
143 ft = f.data_table 144 tt = self.tables[f.file_name_short] 145 tt.num_columns = ft.num_columns 146 tt.num_rows = ft.num_rows 147 tt.data = np.zeros((tt.num_rows, tt.num_columns)) 148 a = self.parameters['a'].value 149 b = self.parameters['b'].value 150 tt.data[:, 0] = ft.data[:, 0] # x values 151 tt.data[:, 1] = a * ft.data[:, 0] + b # y values
Hint
The file type of
ApplicationXY
defined in section New file type tells us that there are two columns in the data files. Hence, the theory data also have two columns to populate. For example of application/theory using more than two data columns, seeclass BaseApplicationLVE
ofApplicationLVE.py
andclass BaseTheoryMaxwellModesFrequency
ofTheoryMaxwellModes.py
.The information from the data file header, in our example
date
andT
, can be called via, e.g.T = float(f.file_parameters["T"])
. Parameters are stored as strings, hence thefloat
conversion.
Note
The new “Line” theory is ready to be used in our new ApplicationXY!
New Icons¶
Application icons are stored in a compiled resource file
gui/MainWindow_rc.py
.
In order to add a new icon to this resource file, that can later be used as
a button icon for instance, we need to
Modify the file
gui/MainWindow.qrc
by opening it in a text editor and add the relative path of the new image or icon we want to have in the resource file. For instance:copy and paste you favourite icon
my_favourite_icon.png
in thegui/Images/new_icons/
folder.add the line
<file>Images/new_icons/my_favourite_icon.png</file>
to the filegui/MainWindow.qrc
Re-compile the file
MainWindow_rc.py
into a resource fileMainWindow_rc.py
by running the following command in a terminal (assuming the current working directory isgui/
)$ pyrcc5 MainWindow.qrc -o MainWindow_rc.py
Note
Your new icon my_favourite_icon.png
is now ready to be used
by Qt:
icon = QIcon(':/Icons/Images/new_icons/my_favourite_icon.png')