I'm using Pymol to measure the distance between two atoms of a nucleic acid. I have a pdb. file from molecular dynamics calculation. When I open that file in pymol I have multiple states of the molecule. I want to measure the distance between two specific atoms and for each state, and export the distances to a file. Then I can visualise the distribution in distance. Can anyone please suggest a method to do this using pymol.
my attempt , code using 1m82.pdb
as example molecule and
distance("dist_1m82" , "/1m82/A/A/U`22/P" , "/1m82/A/A/U`22/N1" , mode = 0)
as distance command (you could use get_distance
if you don't want/need to create a distance object:
#!/usr/bin/env python3
import pymol
from pymol import (
cmd ,
)
pdb_code = '1m82'
print('########## PYMOL VERSION ##########################################')
print(' ', cmd.get_version() )
print('###################################################################')
pymol.finish_launching()
# cmd.feedback("enable" , "all" , "blather")
cmd.set('internal_gui_width' , '500')
# cmd.load('1m82.pdb' , '1m82' , state = 0)
cmd.fetch(pdb_code , pdb_code , state = 0 , quiet = 0 , async_ = 0)
number_of_states = cmd.count_states(pdb_code)
print('states_count -----> ' , number_of_states)
cmd.split_states(pdb_code)
cmd.sync()
cmd.delete(pdb_code)
distances = []
for state_num in range(1, number_of_states + 1):
obj_name = pdb_code+ "_" + str(state_num).zfill(4)
## PyMOLWiki doesnt show default mode number for distance command https://pymolwiki.org/index.php/Distance
dis = cmd.distance("dist_"+obj_name , '/'+obj_name+"/A/A/U`22/P" , '/'+obj_name+"/A/A/U`22/N1" , mode = 0)
distances.append(('dist_'+obj_name , dis))
print('\n ---------\n')
for dist in distances:
print(dist)
output:
########## PYMOL VERSION ##########################################
('2.3.0' ....
states_count -----> 31
---------
('dist_1m82_0001', 5.300721168518066)
('dist_1m82_0002', 5.251534938812256)
('dist_1m82_0003', 5.176976203918457)
('dist_1m82_0004', 5.227502822875977)
('dist_1m82_0005', 4.940760135650635)
('dist_1m82_0006', 5.036609649658203)
('dist_1m82_0007', 5.408339977264404)
('dist_1m82_0008', 5.204244613647461)
('dist_1m82_0009', 5.257850646972656)
('dist_1m82_0010', 5.196445941925049)
('dist_1m82_0011', 5.122419357299805)
('dist_1m82_0012', 5.103713512420654)
('dist_1m82_0013', 5.154935836791992)
('dist_1m82_0014', 5.303188323974609)
('dist_1m82_0015', 5.011679649353027)
('dist_1m82_0016', 5.331982612609863)
('dist_1m82_0017', 5.318058967590332)
('dist_1m82_0018', 5.257812023162842)
('dist_1m82_0019', 5.262117385864258)
('dist_1m82_0020', 5.251483917236328)
('dist_1m82_0021', 5.132598400115967)
('dist_1m82_0022', 5.1809163093566895)
('dist_1m82_0023', 5.067441463470459)
('dist_1m82_0024', 5.514594078063965)
('dist_1m82_0025', 5.220001697540283)
('dist_1m82_0026', 5.40653133392334)
('dist_1m82_0027', 5.2190141677856445)
('dist_1m82_0028', 5.212803363800049)
('dist_1m82_0029', 5.346554279327393)
('dist_1m82_0030', 5.3021650314331055)
('dist_1m82_0031', 5.262298107147217)
picture , distances are drawn as light yellow dashed lines :
As per the
Then I can visualise the distribution in distance
part of your question, I need to create a .py
file like:
runnable.py
:
#!/usr/bin/env python3
import matplotlib.pyplot as plt
y = [i[1] for i in stored.distances]
print('\n\ny : \n' , y ,'\n\n')
fig, axs = plt.subplots()
axs.hist(y , bins = 5)
plt.show()
then add at the end of my first script these lines :
stored.distances = distances
cmd.do('run runnable.py')
of course runnable.py
is in the same folder of my main script
this will get you an extra window a matplotlib one like this one:
where, this need to be checked you'll find the distribution of the calculated distance.
Unfortunately you get an Error too, better a warning like:
runnable.py:17: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
fig, axs = plt.subplots()
runnable.py:21: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
.....
....and many other lines.....
but at least it'll run fast.
Trying to put the runnable.py
inside main script or reverting to use :
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
will result in:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is ........, parent's thread is QThread(0x7fa9a800cd40), current thread is QThread(0x30374d0)
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
with the window showinh the right histogram (the one above), at this point a pymol
window very slow to load and sending out the message above tens of time.
I guess this second part needs more time to be answered.
Addendum
redefining runnable.py
as runnable_2.py
so that my script carries:
stored.distances = distances
cmd.do('run runnable_2.py')
and runnable_2.py
, copied and adapted from https://www.pythonguis.com/tutorials/plotting-matplotlib/is:
import matplotlib
matplotlib.use('Qt5Agg')
# from PyQt5 import QtCore, QtWidgets
from pymol.Qt import QtWidgets, QtCore
print('\n\n###################################\n')
print(' QtCore.qVersion( ---> ' ,QtCore.qVersion())
print('\n\n###################################\n')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
super(MplCanvas, self).__init__(fig)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, values , *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.y = values
# Create the maptlotlib FigureCanvas object,
# which defines a single set of axes as self.axes.
sc = MplCanvas(self, width=5, height=4, dpi=100)
sc.axes.hist(self.y , bins = 5)
self.setCentralWidget(sc)
self.show()
# matplotlib.plot.show()
y = [i[1] for i in stored.distances]
w = MainWindow(y)
I get the histogram depicted above (now being a PyMOL window , not a matplotlib one) without any sort of error.
While using from pymol.Qt import QtWidgets, QtCore
and the runnable_2.py
QWidgets inside the main script (so not started by cmd.do('run
, ...)`
keeps being slow to present the histogram and carries the:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is ...........), parent's thread is QThread(0x7f5ddc00cd40), current thread is QThread(0x30ae7b0)
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
I guess I am missing something like this post for pymol using PyQt:
Create a new Tk window/thread in the PyMOL session and output matplotlib graph on it