In [1]:
from rdkit import Chem
from rdkit.Chem import Draw, rdMolTransforms
import ipywidgets as widgets
from traitlets import Unicode, Int, validate
class MolSVGWidget(widgets.DOMWidget):
    _view_name = Unicode('MolSVGView').tag(sync=True)
    _view_module = Unicode('molsvg_widget').tag(sync=True)
    _view_module_version = Unicode('0.0.1').tag(sync=True)
    
    svg = Unicode('', help="svg to be rendered").tag(sync=True)
    selected_atoms = Unicode('', help="list of currently selected atoms").tag(sync=True)
In [2]:
%%javascript
// make sure our module is only defined
// only once.
require.undef('molsvg_widget');

// Define the `molsvg_widget` module using the Jupyter widgets framework.
define('molsvg_widget', ["@jupyter-widgets/base"],
       function(widgets) {

    // The frontend class:
    var MolSVGView = widgets.DOMWidgetView.extend({

        // This method creates the HTML widget.
        render: function() {
            this.svg_div = document.createElement('div');
            this.el.appendChild(this.svg_div);
            this.model.on('change:svg', this.svg_changed, this);
            this.svg_changed();
        },
        
        // called when the SVG is updated on the Python side
        svg_changed: function() {
            var txt = this.model.get('svg'); 
            this.svg_div.innerHTML = txt;
            var sels = this.svg_div.getElementsByClassName("atom-selector");
            for(var i=0;i<sels.length;i++){
                sels[i].onclick = (evt) => { return this.atom_clicked(evt) };
            }
            
        },

        // callback for when an atom is clicked
        atom_clicked: function(evt) {
            //alert("  "+evt+"|"+this);
            if(!evt.currentTarget.getAttribute('class')){
                return;
            }
            var satmid = evt.currentTarget.getAttribute('class').match(/atom-([0-9]+)/);
            if(satmid.length >1){
                var atmid = Number(satmid[1]);
                var curSel = this.model.get('selected_atoms');
                var splitSel = curSel.split(',');
                var selItms = [];
                var idx = -1;
                //alert("|"+atmid+"|"+curSel+"|len: "+splitSel.length);
                if(curSel != "" && splitSel.length>0){
                    selItms = Array.from(splitSel).map(item => Number(item));
                    idx = selItms.indexOf(atmid);
                }
                if(idx == -1){
                    selItms = selItms.concat(atmid);
                    evt.currentTarget.style["stroke-width"]=3;
                    evt.currentTarget.style["stroke-opacity"]=1;
                    evt.currentTarget.style["stroke"]='#AA22FF';
                } else {
                    selItms.splice(idx,1);
                    evt.currentTarget.style["stroke-width"]=1;
                    evt.currentTarget.style["stroke-opacity"]=0;
                    evt.currentTarget.style["stroke"]='#FFFFFF';
                }
                this.model.set('selected_atoms',String(selItms));
                this.touch();
            }
        }

    });

    return {
        MolSVGView : MolSVGView
    };
});
In [3]:
def mol_to_svg(mol):
    d = Draw.MolDraw2DSVG(350, 200)
    d.SetFontSize(0.8)
    d.DrawMolecule(mol)
    d.TagAtoms(mol)
    d.FinishDrawing()
    return d.GetDrawingText()
In [4]:
bilastine_smi = "O=C(O)C(c1ccc(cc1)CCN4CCC(c2nc3ccccc3n2CCOCC)CC4)(C)C"
In [5]:
bilastine = Chem.MolFromSmiles(bilastine_smi)
dbilastine = Draw.PrepareMolForDrawing(bilastine)
In [6]:
w = MolSVGWidget(svg=mol_to_svg(dbilastine))
In [7]:
w
Out[17]:
In [8]:
w.selected_atoms
Out[8]:
'10,11,12,13'
In [9]:
selected_i = [int(i) for i in w.selected_atoms.split(',')]
In [10]:
selected_i
Out[10]:
[10, 11, 12, 13]
In [11]:
rdMolTransforms.SetDihedralDeg(dbilastine.GetConformer(), *selected_i,
    rdMolTransforms.GetDihedralDeg(dbilastine.GetConformer(), *selected_i) + 180)
In [12]:
w = MolSVGWidget(svg=mol_to_svg(dbilastine))
In [13]:
w
Out[17]:
In [ ]: