Application tutorial¶
VisuaLife has a module named widget which contains components for creating applications for protein analysis. In this tutorial you will see how to display various type of protein data and how to ensure communication between widgets. Application described in this tutorial is availble here.
What data can VisuaLife display?¶
- protein sequence by SequenceViewer
- protein 3D structure by StructureViewer
- protein secondary structure by SecondaryStructureViewer
- binding sites by SequenceViewer or SequenceFeaturesBar
- Multiple Sequence Alignment by MSAViewer
Today you will learn about first four widgets but if you would like to know more about MSAViewer don’t hesitate to look at its documentation.
Getting and parsing data¶
Use ../api/visualife.widget.AjaxSender to get the data that you need. This object takes URL as a first argument and a function to call after receiving the data. If you want to get file from your computer URL will be a path to this file:
i_seq = msa_json[i]["sequence"]
“GET” is an optional argument that indicates request type. Default is “POST”. More about HTTP requests you can find here.
AjaxCommunication widget is often used to fetch a protein structure in the PDB format. This can be done by the following snippet:
def receive_structure(evt = None):
print(evt.text.split("\n")[:10])
AjaxCommunication("https://files.rcsb.org/view/%s.pdb" % pdb_code, receive_structure)()
pdb_code variable provides the code of a protein to be acquired (e.g. 2gb1) and receive_structure()
is the callback method that is called when AjaxCommunication finally receives the data. The protein
comes as text (in the PDB format) format and it’s receive_structure() responsibility to process it further.
Here it just breaks the text into lines and prints the top 10 lines of the file.
Presented application was made in a way that it can work from every computer so the data should be download from other websites. We use https://www.ebi.ac.uk and https://www.rcsb.org which allows to get their files with “GET” request and no CORS error. We wanted to analyse every possible PDB file, so we added input window where you can type your PDB code. Below there is a part of code responsible for getting this data.
AjaxCommunication("https://www.ebi.ac.uk/pdbe/api/pdb/entry/residue_listing/%s" % pdb_name, set_first_res, "GET")()
AjaxCommunication("https://www.ebi.ac.uk/pdbe/api/pdb/entry/molecules/%s" % pdb_name, receive_sequence, "GET")()
AjaxCommunication("https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/%s" % pdb_name, receive_protein_description, "GET")()
After receiving the respond, callback function from AjaxSender constructor is called. In this function you will need to decode the data with json.loads() function which converts JSON string to python structures as lists and dictionaries. Then you will parse the data and get the info that you need to eg. create widgets. In the below example the variable first_res_id is set from received data.
def set_first_res(req):
"""Sets the number of first residue in the chain"""
global pdb_name, first_res_id
data = json.loads(req.text)
first_res_id = data[pdb_name]["molecules"][0]["chains"][0]["residues"][0]["author_residue_number"]
Creating widgets¶
One thing you need to be careful about is that the order of receiving all these responses is unknown, as well as the order of function calls. Sometimes you need to already have some variable set, before other function is called. If this is the case, then it is better to send some request later, not all at once.
In this application we need to know the number of residues to create secondary structure string. This is why we will call ss2_ajax() after setting N_res variable.
def receive_sequence(ajax):
"""Sets the sequence and calls download of binding site and secondary structure"""
global N_res, sequence, resi_repr, pdb_name
d = json.loads(ajax.text)
sequence = d[pdb_name][0]["sequence"]
N_res = len(sequence)
resi_repr = [None for i in range(N_res)]
binding = AjaxCommunication("https://www.ebi.ac.uk/pdbe/api/pdb/entry/binding_sites/%s" % pdb_name, binding_site, "GET")
ss2_ajax()
binding()
We already have a sequence but because we are going to mark binding sites on the SequenceViewer, we will also send a request about it. After receiving the data SequenceViewer will be created and we will be sure to have the right order of calls.
Let’s look on the binding_site() function (called after receiving the data - last line in the code above)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def binding_site(req):
"""Gets info about binding sites and creates according regions on SequenceViewer"""
global sequence, pdb_name, ss2_str, E, H
seq = SequenceViewer("show_sequence", pdb_name, sequence, n_columns_of_ten=4, first_residue_id=first_res_id)
seq.secondary_structure = ss2_str
seq.set_event_callback("CLICK_ON_LETTER", click_on_aa)
seq.regions_palette = "accent"
output = json.loads(req.text)[pdb_name]
for site in output:
desc = site["details"].split()
desc_str = ""
for i in desc[:4]:
desc_str += i.lower() + " "
for i in desc[4:]:
desc_str += i + " "
i = 0
if desc[-3] in ["GOL", "PO4", "SO4", "HOH"]: continue # --- remove obvious buffer compounds from the list of ligands
for res in site["site_residues"]:
i += 1
aa = res["author_residue_number"]
|
In the 4th line SequenceViewer widget is created. In the next line secondary structure is set (to make it possible to colour the sequence with this info). In the line no. 6 function click_on_aa() is set as a callback - this function will be called after clicking on the letter in the sequence (more about it in the next paragraph). In the next line colour pallete is set for colouring the regions. In lines 8-20 received data are parsed according to needs and finally in line no. 21 proper aminoacides from binding sites were added to the corresponding regions.
Communication between widgets¶
Now let’s go back to setting callback function in SequenceViewer. Function is called after clicking on an aminoacid. Every function which is called after firing an event - takes this event as an argument. From the event we can get event target’s id, which will be necessery to conclude which aminoacid was clicked. Having this information we can transfer it to StructureViewer and change style of this aminoacid in this widget. It allows for example to select every aminoacid from one binding site and see how it looks like in the 3D structure.
1 2 3 4 5 6 7 8 9 10 11 12 | def click_on_aa(evt):
"""Function called when you click on a letter in amino acid sequence"""
global panel3d, sequence, resi_repr
pos = int(evt.target.id.split("-")[3]) - 1 # evt.target.id is like: "ch-show_sequence-15" for pos 15
if pos in residues_clicked:
evt.target.style.backgroundColor = residues_clicked[pos]
panel3d.remove_style("1",resi_repr[pos])
del(residues_clicked[pos])
else:
resi_repr[pos] = panel3d.add_style("1", "licorice", {"color": "atomindex", "sele": "%d" % (pos + first_res_id)})
residues_clicked[pos] = evt.target.style.backgroundColor
evt.target.style.backgroundColor = "yellow"
|
Because we want to change the style back after second click we are storing style info about selected aminoacides in a dictionary. After second click we can go back to previous representation in StructureViewer and unselect aminoacid in SequenceViewer.
That’s all! If you need further information, please look at widget documentation and Examples gallery.