1 #!/bin/env python 2 # 3 # File: RDKitDrawMolecules.py 4 # Author: Manish Sud <msud@san.rr.com> 5 # 6 # Copyright (C) 2025 Manish Sud. All rights reserved. 7 # 8 # The functionality available in this script is implemented using RDKit, an 9 # open source toolkit for cheminformatics developed by Greg Landrum. 10 # 11 # This file is part of MayaChemTools. 12 # 13 # MayaChemTools is free software; you can redistribute it and/or modify it under 14 # the terms of the GNU Lesser General Public License as published by the Free 15 # Software Foundation; either version 3 of the License, or (at your option) any 16 # later version. 17 # 18 # MayaChemTools is distributed in the hope that it will be useful, but without 19 # any warranty; without even the implied warranty of merchantability of fitness 20 # for a particular purpose. See the GNU Lesser General Public License for more 21 # details. 22 # 23 # You should have received a copy of the GNU Lesser General Public License 24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 26 # Boston, MA, 02111-1307, USA. 27 # 28 29 from __future__ import print_function 30 31 # Add local python path to the global path and import standard library modules... 32 import os 33 import sys; sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "..", "lib", "Python")) 34 import time 35 import re 36 37 # RDKit imports... 38 try: 39 from rdkit import rdBase 40 from rdkit import Chem 41 from rdkit.Chem import AllChem 42 from rdkit.Chem import Draw 43 from rdkit.Chem.Draw.MolDrawing import DrawingOptions 44 except ImportError as ErrMsg: 45 sys.stderr.write("\nFailed to import RDKit module/package: %s\n" % ErrMsg) 46 sys.stderr.write("Check/update your RDKit environment and try again.\n\n") 47 sys.exit(1) 48 49 # MayaChemTools imports... 50 try: 51 from docopt import docopt 52 import MiscUtil 53 import RDKitUtil 54 except ImportError as ErrMsg: 55 sys.stderr.write("\nFailed to import MayaChemTools module/package: %s\n" % ErrMsg) 56 sys.stderr.write("Check/update your MayaChemTools environment and try again.\n\n") 57 sys.exit(1) 58 59 ScriptName = os.path.basename(sys.argv[0]) 60 Options = {} 61 OptionsInfo = {} 62 63 def main(): 64 """Start execution of the script.""" 65 66 MiscUtil.PrintInfo("\n%s (RDKit v%s; MayaChemTools v%s; %s): Starting...\n" % (ScriptName, rdBase.rdkitVersion, MiscUtil.GetMayaChemToolsVersion(), time.asctime())) 67 68 (WallClockTime, ProcessorTime) = MiscUtil.GetWallClockAndProcessorTime() 69 70 # Retrieve command line arguments and options... 71 RetrieveOptions() 72 73 # Process and validate command line arguments and options... 74 ProcessOptions() 75 76 # Perform actions required by the script... 77 DrawMolecules() 78 79 MiscUtil.PrintInfo("\n%s: Done...\n" % ScriptName) 80 MiscUtil.PrintInfo("Total time: %s" % MiscUtil.GetFormattedElapsedTime(WallClockTime, ProcessorTime)) 81 82 def DrawMolecules(): 83 """Draw molecules.""" 84 85 Infile = OptionsInfo["Infile"] 86 Outfile = OptionsInfo["Outfile"] 87 88 # Read molecules... 89 MiscUtil.PrintInfo("\nReading file %s..." % Infile) 90 91 ValidMols, MolCount, ValidMolCount = RDKitUtil.ReadAndValidateMolecules(Infile, **OptionsInfo["InfileParams"]) 92 93 MiscUtil.PrintInfo("Total number of molecules: %d" % MolCount) 94 MiscUtil.PrintInfo("Number of valid molecules: %d" % ValidMolCount) 95 MiscUtil.PrintInfo("Number of ignored molecules: %d" % (MolCount - ValidMolCount)) 96 97 # Compute 2D coordinates... 98 if OptionsInfo["Compute2DCoords"]: 99 MiscUtil.PrintInfo("\nComputing 2D coordinates...") 100 for Mol in ValidMols: 101 AllChem.Compute2DCoords(Mol) 102 103 MiscUtil.PrintInfo("Generating image grid...") 104 105 # Setup atoms lists for highlighting atoms and bonds... 106 AtomLists = SetupAtomListsToHighlight(ValidMols) 107 BondLists = None 108 109 # Set up legends... 110 MolNames = None 111 if OptionsInfo["ShowMolName"]: 112 MolNames = [] 113 MolCount = 0 114 for Mol in ValidMols: 115 MolCount += 1 116 MolName = RDKitUtil.GetMolName(Mol, MolCount) 117 MolNames.append(MolName) 118 119 # Perform alignment to a common template... 120 PerformAlignment(ValidMols) 121 122 # Generate appropriate output files... 123 if MiscUtil.CheckFileExt(Outfile, "svg"): 124 GenerateSVGImageFile(ValidMols, MolNames, AtomLists, BondLists) 125 elif MiscUtil.CheckFileExt(Outfile, "html htm"): 126 GenerateHTMLTableFile(ValidMols, MolNames, AtomLists, BondLists) 127 else: 128 GenerateImageFile(ValidMols, MolNames, AtomLists, BondLists) 129 130 def GenerateSVGImageFile(ValidMols, MolNames, AtomLists, BondLists): 131 """Generate a SVG image file.""" 132 133 MolsSVGText = RDKitUtil.GetSVGForMolecules(ValidMols, OptionsInfo["NumOfMolsPerRow"], OptionsInfo["MolImageWidth"], OptionsInfo["MolImageHeight"], Legends = MolNames, AtomListsToHighlight = AtomLists, BondListsToHighlight = BondLists, BoldText = OptionsInfo["FontBold"]) 134 135 MiscUtil.PrintInfo("\nGenerating SVG image file %s..." % OptionsInfo["Outfile"]) 136 137 OutFH = open(OptionsInfo["Outfile"], "w") 138 OutFH.write(MolsSVGText) 139 OutFH.close() 140 141 def GenerateImageFile(ValidMols, MolNames, AtomLists, BondLists): 142 """Generate a non SVG image file.""" 143 144 Outfile = OptionsInfo["Outfile"] 145 146 NumOfMolsPerRow = OptionsInfo["NumOfMolsPerRow"] 147 Width = OptionsInfo["MolImageWidth"] 148 Height = OptionsInfo["MolImageHeight"] 149 150 # Setup drawing options... 151 UpdatedDrawingOptions = DrawingOptions() 152 UpdatedDrawingOptions.atomLabelFontSize = int(OptionsInfo["AtomLabelFontSize"]) 153 UpdatedDrawingOptions.bondLineWidth = float(OptionsInfo["BondLineWidth"]) 154 155 try: 156 MolsImage = Draw.MolsToGridImage(ValidMols, molsPerRow = NumOfMolsPerRow, subImgSize = (Width,Height), legends = MolNames, highlightAtomLists = AtomLists, highlightBondLists = BondLists, useSVG = False, kekulize = OptionsInfo["Kekulize"], options = UpdatedDrawingOptions) 157 except Exception as ErrMsg: 158 # MolsToGridImage doesn't appear to handle the following parameters in the latest version of RDKit: 159 # . kekulize = OptionsInfo["Kekulize"], options = UpdatedDrawingOptions 160 MolsImage = Draw.MolsToGridImage(ValidMols, molsPerRow = NumOfMolsPerRow, subImgSize = (Width,Height), legends = MolNames, highlightAtomLists = AtomLists, highlightBondLists = BondLists, useSVG = False, returnPNG = False) 161 162 MiscUtil.PrintInfo("\nGenerating image file %s..." % Outfile) 163 164 if MiscUtil.CheckFileExt(Outfile, "pdf"): 165 if MolsImage.mode == 'RGBA': 166 MolsImage = MolsImage.convert('RGB') 167 168 MolsImage.save(Outfile) 169 170 def GenerateHTMLTableFile(ValidMols, MolNames, HighlightAtomLists, HighlightBondLists): 171 """Generate a HTML table file.""" 172 173 Outfile = OptionsInfo["Outfile"] 174 175 Writer = open(Outfile, "w") 176 if Writer is None: 177 MiscUtil.PrintError("Failed to setup a writer for output fie %s " % Outfile) 178 179 MiscUtil.PrintInfo("\nGenerating HTML table file %s..." % Outfile) 180 181 WriteHTMLPageHeader(Writer, len(ValidMols)) 182 WriteHTMLPageTitle(Writer) 183 184 WriteHTMLTableHeader(Writer) 185 WriteHTMLTableRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists) 186 WriteHTMLTableEnd(Writer) 187 188 WriteHTMLPageFooter(Writer) 189 WriteHTMLPageEnd(Writer) 190 191 if Writer is not None: 192 Writer.close() 193 194 def WriteHTMLTableRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists): 195 """Write out HTML table rows.""" 196 197 WriteTableHeaderRow(Writer, ValidMols) 198 WriteTableDataRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists) 199 WriteTableFooterRow(Writer, ValidMols) 200 201 def WriteTableDataRows(Writer, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists): 202 """Write out table data row.""" 203 204 Writer.write(""" <tbody>\n""") 205 206 MolCount = len(ValidMols) 207 ColCount = GetColCount(MolCount) 208 209 for Index in range(0, MolCount, ColCount): 210 Writer.write(""" <tr>\n""") 211 212 if OptionsInfo["CounterCol"]: 213 Writer.write(""" <td></td>\n""") 214 215 for MolIndex in range(Index, (Index + ColCount)): 216 SetupStructureDataDrawing(Writer, MolIndex, ValidMols, MolNames, HighlightAtomLists, HighlightBondLists) 217 218 Writer.write(""" </tr>\n""") 219 220 Writer.write(""" </tbody>\n""") 221 222 def SetupStructureDataDrawing(Writer, MolIndex, Mols, MolNames, HighlightAtomLists, HighlightBondLists): 223 """Setup structure data drawing for a tabel cell.""" 224 225 if MolIndex >= len(Mols): 226 Writer.write(""" <td></td>\n""") 227 return 228 229 Mol = Mols[MolIndex] 230 MolName = None if MolNames is None else MolNames[MolIndex] 231 HighlightAtomList = None if HighlightAtomLists is None else HighlightAtomLists[MolIndex] 232 HighlightBondList = None if HighlightBondLists is None else HighlightBondLists[MolIndex] 233 234 SVGText = RDKitUtil.GetInlineSVGForMolecule(Mol, OptionsInfo["MolImageWidth"], OptionsInfo["MolImageHeight"], Legend = MolName, AtomListToHighlight = HighlightAtomList, BondListToHighlight = HighlightBondList, BoldText = OptionsInfo["FontBold"], Base64Encoded = OptionsInfo["MolImageEncoded"]) 235 236 PopoverTag = GetMolPopoverTag(Mol) 237 ImageTag = "img" if PopoverTag is None else "img %s" % PopoverTag 238 239 if OptionsInfo["MolImageEncoded"]: 240 SVGInlineImageTag = "%s src=\"data:image/svg+xml;base64,\n%s\"" % (ImageTag, SVGText) 241 else: 242 SVGInlineImageTag = "%s src=\"data:image/svg+xml;charset=UTF-8,\n%s\"" % (ImageTag, SVGText) 243 244 Writer.write(""" <td bgcolor="white"><%s></td>\n""" % SVGInlineImageTag) 245 246 def WriteTableHeaderRow(Writer, ValidMols): 247 """Write out table header row.""" 248 249 if not OptionsInfo["TableHeader"]: 250 return 251 252 TableHeaderStyle = OptionsInfo["TableHeaderStyle"] 253 if TableHeaderStyle is None: 254 Writer.write(""" <thead>\n""") 255 Writer.write(""" <tr>\n""") 256 elif re.match("^(thead|table)", TableHeaderStyle): 257 Writer.write(""" <thead class="%s">\n""" % TableHeaderStyle) 258 Writer.write(""" <tr>\n""") 259 else: 260 Writer.write(""" <thead>\n""") 261 Writer.write(""" <tr bgcolor="%s"\n""" % TableHeaderStyle) 262 263 if OptionsInfo["CounterCol"]: 264 Writer.write(""" <th></th>\n""") 265 266 # Write out rest of the column headers... 267 MolCount = len(ValidMols) 268 ColCount = GetColCount(MolCount) 269 for ColIndex in range(0, ColCount): 270 ColLabel = MiscUtil.GetExcelStyleColumnLabel(ColIndex + 1) 271 Writer.write(""" <th>%s</th>\n""" % ColLabel) 272 273 Writer.write(""" </tr>\n""") 274 Writer.write(""" </thead>\n""") 275 276 def WriteTableFooterRow(Writer, ValidMols): 277 """Write out table footer row.""" 278 279 if not OptionsInfo["TableFooter"]: 280 return 281 282 Writer.write(""" <tfoot>\n""") 283 Writer.write(""" <tr>\n""") 284 285 if OptionsInfo["CounterCol"]: 286 Writer.write(""" <td></td>\n""") 287 288 # Write out rest of the column headers... 289 MolCount = len(ValidMols) 290 ColCount = GetColCount(MolCount) 291 for ColIndex in range(0, ColCount): 292 ColLabel = MiscUtil.GetExcelStyleColumnLabel(ColIndex + 1) 293 Writer.write(""" <td>%s</td>\n""" % ColLabel) 294 295 Writer.write(""" </tr>\n""") 296 Writer.write(""" </tfoot>\n""") 297 298 def WriteHTMLPageHeader(Writer, MolCount): 299 """Write out HTML page header.""" 300 301 ColCount = GetColCount(MolCount) 302 303 # Exclude counter and structure columns from sorting and searching... 304 if OptionsInfo["CounterCol"]: 305 ColIndicesList = ["0"] 306 ColVisibilityExcludeColIndicesList = ["0"] 307 ColIndexOffset = 1 308 FreezeLeftColumns = "1" 309 else: 310 ColIndicesList = [] 311 ColVisibilityExcludeColIndicesList = [] 312 ColIndexOffset = 0 313 314 MaxDataColVisColCount = 25 315 for Index in range(0, ColCount): 316 ColIndex = Index + ColIndexOffset 317 ColIndicesList.append("%s" % ColIndex) 318 319 if OptionsInfo["ColVisibility"]: 320 if Index >= MaxDataColVisColCount: 321 ColVisibilityExcludeColIndicesList.append("%s" %ColIndex) 322 323 ColIndices = MiscUtil.JoinWords(ColIndicesList, ", ") if len(ColIndicesList) else "" 324 ColVisibilityExcludeColIndices = MiscUtil.JoinWords(ColVisibilityExcludeColIndicesList, ", ") if len(ColVisibilityExcludeColIndicesList) else "" 325 326 DataColVisibilityExclude = False 327 if OptionsInfo["ColVisibility"]: 328 if ColCount > MaxDataColVisColCount: 329 DataColVisibilityExclude = True 330 MiscUtil.PrintWarning("The number of data columns, %d, is quite large. Only first %d data columns will be available in column visibility pulldown." % (ColCount, MaxDataColVisColCount)) 331 332 DisplayButtons = False 333 if OptionsInfo["ColVisibility"]: 334 if ColCount > 0: 335 DisplayButtons = True 336 337 FreezeCols = False 338 if (OptionsInfo["CounterCol"] and OptionsInfo["ScrollX"]): 339 FreezeCols = True 340 341 Paging = "true" if OptionsInfo["Paging"] else "false" 342 PageLength = "%d" % OptionsInfo["PageLength"] 343 PagingType = "\"%s\"" % OptionsInfo["PagingType"] 344 345 ScrollX = "true" if OptionsInfo["ScrollX"] else "false" 346 347 ScrollY = "" 348 if OptionsInfo["ScrollY"]: 349 if re.search("vh$", OptionsInfo["ScrollYSize"]): 350 ScrollY = "\"%s\"" % OptionsInfo["ScrollYSize"] 351 else: 352 ScrollY = "%s" % OptionsInfo["ScrollYSize"] 353 354 # Start HTML header... 355 Title = "Molecules table" if OptionsInfo["Header"] is None else OptionsInfo["Header"] 356 357 Writer.write("""\ 358 <!doctype html> 359 <html lang="en"> 360 <head> 361 <title>%s</title> 362 <meta charset="utf-8"> 363 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 364 <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> 365 <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.16/css/dataTables.bootstrap4.min.css"> 366 367 """ % (Title)) 368 369 if (FreezeCols): 370 Writer.write("""\ 371 <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/fixedcolumns/3.2.4/css/fixedColumns.bootstrap4.min.css"> 372 """) 373 374 if (OptionsInfo["KeysNavigation"]): 375 Writer.write("""\ 376 <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/keytable/2.3.2/css/keyTable.bootstrap4.min.css"> 377 """) 378 379 Writer.write("""\ 380 381 <script type="text/javascript" language="javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script> 382 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script> 383 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/1.10.16/js/dataTables.bootstrap4.min.js"></script> 384 385 """) 386 387 if (OptionsInfo["Popover"]): 388 Writer.write("""\ 389 <script type="text/javascript" language="javascript" src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script> 390 <script type="text/javascript" language="javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> 391 392 """) 393 394 if DisplayButtons: 395 Writer.write("""\ 396 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/buttons/1.5.1/js/dataTables.buttons.min.js"></script> 397 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.bootstrap4.min.js"></script> 398 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/buttons/1.5.1/js/buttons.colVis.min.js"></script> 399 400 """) 401 402 if (FreezeCols): 403 Writer.write("""\ 404 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/fixedcolumns/3.2.4/js/dataTables.fixedColumns.min.js"></script> 405 """) 406 407 if (OptionsInfo["KeysNavigation"]): 408 Writer.write("""\ 409 <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/keytable/2.3.2/js/dataTables.keyTable.min.js"></script> 410 """) 411 412 # Intialize table using Bootstrap, DataTables and JQuery frameworks... 413 Writer.write("""\ 414 415 <script type="text/javascript" class="init"> 416 417 $(document).ready(function() { 418 """) 419 420 if (OptionsInfo["Popover"]): 421 Writer.write("""\ 422 $('.MolPopover').popover(); 423 424 """) 425 426 Writer.write("""\ 427 var MolsTable = $('#MolsTable').DataTable( { 428 "columnDefs": [ 429 { 430 "orderable": false, 431 "searchable": false, 432 "targets": [%s] 433 }, 434 """ % (ColIndices)) 435 436 if OptionsInfo["ColVisibility"]: 437 Writer.write("""\ 438 { 439 "className": "noColVisCtrl", 440 "targets": [%s] 441 } 442 """ % (ColVisibilityExcludeColIndices)) 443 444 Writer.write("""\ 445 ], 446 """) 447 448 # Set up dom for displaying button and other options... 449 if OptionsInfo["ColVisibility"]: 450 if OptionsInfo["Paging"]: 451 Writer.write("""\ 452 "dom": "<'row'<'col-sm-6'l><'col-sm-6'<'float-right'B>>>" + 453 "<'row'<'col-sm-12'tr>>" + 454 "<'row'<'col-sm-5'i><'col-sm-7'p>>", 455 """) 456 else: 457 Writer.write("""\ 458 "dom": "<'row'<'col'<'float-right'B>>>" + 459 "<'row'<'col-sm-12'tr>>" + 460 "<'row'<'col-sm-5'i><'col-sm-7'p>>", 461 """) 462 else: 463 Writer.write("""\ 464 "dom": "<'row'<'col'l>>" + 465 "<'row'<'col-sm-12'tr>>" + 466 "<'row'<'col-sm-5'i><'col-sm-7'p>>", 467 """) 468 469 # 470 if OptionsInfo["ColVisibility"]: 471 # Set up buttons... 472 Writer.write("""\ 473 "buttons": [ 474 { 475 "extend": "colvis", 476 "text": "Column visibility", 477 "className": "btn btn-outline-light text-dark", 478 "columns": ":not(.noColVisCtrl)", 479 """) 480 if not DataColVisibilityExclude: 481 Writer.write("""\ 482 "prefixButtons": [ "colvisRestore" ], 483 """) 484 485 Writer.write("""\ 486 "columnText": function ( dt, colIndex, colLabel ) { 487 return "Column " + (colIndex + 1); 488 }, 489 } 490 ], 491 """) 492 493 # Write out rest of the variables for DataTables... 494 if FreezeCols: 495 Writer.write("""\ 496 "fixedColumns": { 497 "leftColumns": %s 498 }, 499 """ % (FreezeLeftColumns)) 500 501 if (OptionsInfo["KeysNavigation"]): 502 Writer.write("""\ 503 "keys": true, 504 """) 505 506 Writer.write("""\ 507 "pageLength": %s, 508 "lengthMenu": [ [5, 10, 15, 25, 50, 100, 500, 1000, -1], [5, 10, 15, 25, 50, 100, 500, 1000, "All"] ], 509 "paging": %s, 510 "pagingType": %s, 511 "scrollX": %s, 512 "scrollY": %s, 513 "scrollCollapse": true, 514 "order": [], 515 } ); 516 """ % (PageLength, Paging, PagingType, ScrollX, ScrollY)) 517 518 if OptionsInfo["CounterCol"]: 519 Writer.write("""\ 520 MolsTable.on( 'order.dt search.dt', function () { 521 MolsTable.column(0, {search:'applied', order:'applied'}).nodes().each( function (cell, rowIndex) { 522 cell.innerHTML = rowIndex + 1; 523 } ); 524 } ).draw(); 525 """) 526 527 # End of Javacscript code... 528 Writer.write("""\ 529 } ); 530 531 </script> 532 """) 533 534 # Finish up HTML header... 535 Writer.write("""\ 536 537 </head> 538 <body> 539 <div class="container-fluid"> 540 <br/> 541 """) 542 543 def WriteHTMLPageEnd(Writer): 544 """Write out HTML page end.""" 545 546 Writer.write("""\ 547 </div> 548 </body> 549 </html> 550 """) 551 552 def WriteHTMLPageTitle(Writer): 553 """Write out HTML page title.""" 554 555 if OptionsInfo["Header"] is None: 556 return 557 558 Writer.write(""" <%s class="text-center">%s</%s>\n""" % (OptionsInfo["HeaderStyle"], OptionsInfo["Header"], OptionsInfo["HeaderStyle"])) 559 560 def WriteHTMLPageFooter(Writer): 561 """Write out HTML page footer.""" 562 563 if OptionsInfo["Footer"] is None: 564 return 565 566 Writer.write(""" <br/>\n <p class="%s">%s</p>\n""" % (OptionsInfo["FooterClass"], OptionsInfo["Footer"])) 567 568 def WriteHTMLTableHeader(Writer): 569 """Write out HTML table header.""" 570 571 if OptionsInfo["TableStyle"] is None: 572 Writer.write("""\n <table id="MolsTable" cellspacing="0" width="100%">\n""") 573 else: 574 Writer.write(""" <table id="MolsTable" class="%s" cellspacing="0" width="100%s">\n""" % (OptionsInfo["TableStyle"], "%")) 575 576 def WriteHTMLTableEnd(Writer): 577 """Write out HTML table end.""" 578 579 Writer.write(""" </table>\n\n""") 580 581 def GetColCount(MolCount): 582 """Get tabke column count.""" 583 584 ColCount = OptionsInfo["NumOfMolsPerRow"] if OptionsInfo["NumOfMolsPerRow"] <= MolCount else MolCount 585 586 return ColCount 587 588 def SetupAtomListsToHighlight(ValidMols): 589 """Set up atom lists to highlight using specified SMARTS pattern.""" 590 591 AtomListsToHighlight = None 592 if OptionsInfo["HighlightSMARTSPattern"] is None: 593 return AtomListsToHighlight 594 595 PatternMol = Chem.MolFromSmarts(OptionsInfo["HighlightSMARTSPattern"]) 596 AtomListsToHighlight = [] 597 for ValidMol in ValidMols: 598 # Get matched atom lists and flatten it... 599 MatchedAtomsLists = ValidMol.GetSubstructMatches(PatternMol) 600 MatchedAtoms = [ Atom for AtomsList in MatchedAtomsLists for Atom in AtomsList] 601 AtomListsToHighlight.append(MatchedAtoms) 602 603 return AtomListsToHighlight 604 605 def PerformAlignment(ValidMols): 606 """Perform alignment to a common template specified by a SMARTS pattern.""" 607 608 if OptionsInfo["AlignmentSMARTSPattern"] is None: 609 return 610 611 PatternMol = Chem.MolFromSmarts(OptionsInfo["AlignmentSMARTSPattern"]) 612 AllChem.Compute2DCoords(PatternMol) 613 614 MatchedValidMols = [ValidMol for ValidMol in ValidMols if ValidMol.HasSubstructMatch(PatternMol)] 615 for ValidMol in MatchedValidMols: 616 AllChem.GenerateDepictionMatching2DStructure(ValidMol, PatternMol) 617 618 def GetMolPopoverTag(Mol): 619 """Set up a popover window containing any additional information about molecule.""" 620 621 if not OptionsInfo["Popover"]: 622 return None 623 624 # Set up data label and values... 625 AvailableDataLabels = Mol.GetPropNames(includePrivate = False, includeComputed = False) 626 627 DataContentLines = [] 628 MaxDataCharWidth = OptionsInfo["PopoverTextWidth"] 629 MaxDataDisplayCount = OptionsInfo["PopoverDataCount"] 630 631 DataDisplayCount = 0 632 SkippedDataDisplay = False 633 for DataLabel in AvailableDataLabels: 634 DataDisplayCount += 1 635 if DataDisplayCount > MaxDataDisplayCount: 636 SkippedDataDisplay = True 637 break 638 639 DataValue = "%s" % Mol.GetProp(DataLabel) 640 DataValue = DataValue.strip() 641 if MiscUtil.IsEmpty(DataValue): 642 continue 643 644 # Change any new lines to ; 645 if re.search("(\r\n|\r|\n)", DataValue): 646 DataValue = re.sub("(\r\n|\r|\n)", "; ", DataValue) 647 648 DataValue = MiscUtil.TruncateText(DataValue, MaxDataCharWidth, "...") 649 DataValue = MiscUtil.ReplaceHTMLEntitiesInText(DataValue) 650 651 DataContent = "<b>%s</b>: %s" % (DataLabel, DataValue) 652 DataContentLines.append(DataContent) 653 654 if not len(DataContentLines): 655 return None 656 657 if SkippedDataDisplay: 658 DataContent = "<b>... ... ...</b>" 659 DataContentLines.append(DataContent) 660 661 DataContent = "Showing 1 to %s of %s" % (MaxDataDisplayCount, len(AvailableDataLabels)) 662 DataContentLines.append(DataContent) 663 else: 664 DataContent = "Showing 1 to %s of %s" % (DataDisplayCount, len(AvailableDataLabels)) 665 DataContentLines.append(DataContent) 666 667 DataContent = MiscUtil.JoinWords(DataContentLines, "<br/>") 668 PopoverTag = """class="MolPopover" data-toggle="popover" data-html="true" data-trigger="click" data-placement="right" title="<span class='small'><b>Additional Information</b></span>" data-content="<span class='small'>%s</span>" """ % DataContent 669 670 return PopoverTag 671 672 def ProcessOptions(): 673 """Process and validate command line arguments and options.""" 674 675 MiscUtil.PrintInfo("Processing options...") 676 677 # Validate options... 678 ValidateOptions() 679 680 OptionsInfo["Infile"] = Options["--infile"] 681 OptionsInfo["Outfile"] = Options["--outfile"] 682 OptionsInfo["Overwrite"] = Options["--overwrite"] 683 684 # No need for any RDKit specific --outfileParams.... 685 OptionsInfo["InfileParams"] = MiscUtil.ProcessOptionInfileParameters("--infileParams", Options["--infileParams"], OptionsInfo["Infile"]) 686 687 AlignmentSMARTSPattern = None 688 if not re.match("^None$", Options["--alignmentSMARTS"], re.I): 689 AlignmentSMARTSPattern = Options["--alignmentSMARTS"] 690 OptionsInfo["AlignmentSMARTSPattern"] = AlignmentSMARTSPattern 691 692 OptionsInfo["AtomLabelFontSize"] = Options["--atomLabelFontSize"] 693 OptionsInfo["BondLineWidth"] = Options["--bondLineWidth"] 694 695 Compute2DCoords = True 696 if re.match("^yes$", Options["--compute2DCoords"], re.I): 697 Compute2DCoords = True 698 elif re.match("^no$", Options["--compute2DCoords"], re.I): 699 Compute2DCoords = False 700 OptionsInfo["Compute2DCoords"] = Compute2DCoords 701 702 CounterCol = True 703 if re.match("^no$", Options["--counterCol"], re.I): 704 CounterCol = False 705 OptionsInfo["CounterCol"] = CounterCol 706 707 ColVisibility = True 708 if re.match("^no$", Options["--colVisibility"], re.I): 709 ColVisibility = False 710 OptionsInfo["ColVisibility"] = ColVisibility 711 712 OptionsInfo["FontBold"] = True 713 if re.match("^no$", Options["--fontBold"], re.I): 714 OptionsInfo["FontBold"] = False 715 716 Footer = None 717 if not re.match("^None$", Options["--footer"], re.I): 718 Footer = Options["--footer"] 719 OptionsInfo["Footer"] = Footer 720 721 FooterClass = Options["--footerClass"].strip() 722 if MiscUtil.IsEmpty(FooterClass): 723 MiscUtil.PrintError("The value specified using option \"--footerClass\" is empty.") 724 OptionsInfo["FooterClass"] = FooterClass 725 726 Header = None 727 if not re.match("^None$", Options["--header"], re.I): 728 Header = Options["--header"] 729 OptionsInfo["Header"] = Header 730 731 HeaderStyle = Options["--headerStyle"].strip() 732 if MiscUtil.IsEmpty(HeaderStyle): 733 MiscUtil.PrintError("The value specified using option \"--headerStyle\" is empty.") 734 OptionsInfo["HeaderStyle"] = HeaderStyle 735 736 HighlightSMARTSPattern = None 737 if not re.match("^None$", Options["--highlightSMARTS"], re.I): 738 HighlightSMARTSPattern = Options["--highlightSMARTS"] 739 OptionsInfo["HighlightSMARTSPattern"] = HighlightSMARTSPattern 740 741 OptionsInfo["Kekulize"] = True 742 if re.match("^no$", Options["--kekulize"], re.I): 743 OptionsInfo["Kekulize"] = False 744 745 OptionsInfo["KeysNavigation"] = True 746 if re.match("^no$", Options["--keysNavigation"], re.I): 747 OptionsInfo["KeysNavigation"] = False 748 749 SizeValues = Options["--molImageSize"].split(",") 750 OptionsInfo["MolImageWidth"] = int(SizeValues[0]) 751 OptionsInfo["MolImageHeight"] = int(SizeValues[1]) 752 753 OptionsInfo["MolImageEncoded"] = True 754 if re.match("^no$", Options["--molImageEncoded"], re.I): 755 OptionsInfo["MolImageEncoded"] = False 756 757 OptionsInfo["NumOfMolsPerRow"] = int(Options["--numOfMolsPerRow"]) 758 759 OptionsInfo["Paging"] = True 760 if re.match("^no$", Options["--paging"], re.I): 761 OptionsInfo["Paging"] = False 762 763 PagingType = Options["--pagingType"] 764 if not re.match("^(numbers|simple|simple_numbers|full|full_numbers|simple_number)$", Options["--pagingType"], re.I): 765 MiscUtil.PrintWarning("The paging type name, %s, specified using option \"--pagingType\" appears to be a unknown type..." % (PagingType)) 766 OptionsInfo["PagingType"] = PagingType.lower() 767 768 OptionsInfo["PageLength"] = int(Options["--pageLength"]) 769 770 OptionsInfo["Popover"] = True 771 if re.match("^no$", Options["--popover"], re.I): 772 OptionsInfo["Popover"] = False 773 OptionsInfo["PopoverDataCount"] = int(Options["--popoverDataCount"]) 774 OptionsInfo["PopoverTextWidth"] = int(Options["--popoverTextWidth"]) 775 776 OptionsInfo["ShowMolName"] = True 777 if re.match("^no$", Options["--showMolName"], re.I): 778 OptionsInfo["ShowMolName"] = False 779 780 OptionsInfo["ScrollX"] = True 781 if re.match("^no$", Options["--scrollX"], re.I): 782 OptionsInfo["ScrollX"] = False 783 784 OptionsInfo["ScrollY"] = True 785 if re.match("^no$", Options["--scrollY"], re.I): 786 OptionsInfo["ScrollY"] = False 787 788 OptionsInfo["ScrollYSize"] = Options["--scrollYSize"] 789 if re.match("vh$", Options["--scrollYSize"], re.I): 790 ScrollYSize = int(re.sub("vh$", "", Options["--scrollYSize"])) 791 if ScrollYSize <= 0: 792 MiscUtil.PrintError("The value specified, %s, for option \"--scrollYSize\" is not valid. Supported value: > 0 followed by \"vh\"" % Options["--scrollYSize"]) 793 794 TableStyle = None 795 if not re.match("^None$", Options["--tableStyle"], re.I): 796 if re.match("^All$", Options["--tableStyle"], re.I): 797 TableStyle = "table table-striped table-bordered table-hover table-dark" 798 else: 799 TableStyle = re.sub(" ", "", Options["--tableStyle"]) 800 for Style in [Style for Style in TableStyle.split(",")]: 801 if not re.match("^(table|table-striped|table-bordered|table-hover|table-dark|table-sm)$", Style, re.I): 802 MiscUtil.PrintWarning("The table style name, %s, specified using option \"-t, --tableStyle\" appears to be a unknown style..." % (Style)) 803 TableStyle = re.sub(",", " ", TableStyle.lower()) 804 OptionsInfo["TableStyle"] = TableStyle 805 806 OptionsInfo["TableFooter"] = True 807 if re.match("^no$", Options["--tableFooter"], re.I): 808 OptionsInfo["TableFooter"] = False 809 810 OptionsInfo["TableHeader"] = True 811 if re.match("^no$", Options["--tableHeader"], re.I): 812 OptionsInfo["TableHeader"] = False 813 814 TableHeaderStyle = None 815 if not re.match("^None$", Options["--tableHeaderStyle"], re.I): 816 TableHeaderStyle = Options["--tableHeaderStyle"] 817 TableHeaderStyle = TableHeaderStyle.lower() 818 CheckOptionTableClassColorValues("--tableHeaderStyle", [TableHeaderStyle]) 819 OptionsInfo["TableHeaderStyle"] = TableHeaderStyle 820 821 def CheckOptionTableClassColorValues(OptionName, ColorsList): 822 """Check names of table color classes and issue a warning for unknown names.""" 823 824 TableClassColors = ["thead-dark", "thead-light", "table-primary", "table-success", "table-danger", "table-info", "table-warning", "table-active", "table-secondary", "table-light", "table-dark", "bg-primary", "bg-success", "bg-danger", "bg-info", "bg-warning", "bg-secondary", "bg-dark", "bg-light"] 825 826 for Color in ColorsList: 827 if not Color in TableClassColors: 828 MiscUtil.PrintWarning("The color class name, %s, specified using option \"%s\" appears to be a unknown name..." % (Color, OptionName)) 829 830 def RetrieveOptions(): 831 """Retrieve command line arguments and options.""" 832 833 # Get options... 834 global Options 835 Options = docopt(_docoptUsage_) 836 837 # Set current working directory to the specified directory... 838 WorkingDir = Options["--workingdir"] 839 if WorkingDir: 840 os.chdir(WorkingDir) 841 842 # Handle examples option... 843 if "--examples" in Options and Options["--examples"]: 844 MiscUtil.PrintInfo(MiscUtil.GetExamplesTextFromDocOptText(_docoptUsage_)) 845 sys.exit(0) 846 847 def ValidateOptions(): 848 """Validate option values.""" 849 850 MiscUtil.ValidateOptionFilePath("-i, --infile", Options["--infile"]) 851 MiscUtil.ValidateOptionFileExt("-i, --infile", Options["--infile"], "sdf sd mol smi csv tsv txt") 852 853 MiscUtil.ValidateOptionsOutputFileOverwrite("-o, --outfile", Options["--outfile"], "--overwrite", Options["--overwrite"]) 854 MiscUtil.ValidateOptionsDistinctFileNames("-i, --infile", Options["--infile"], "-o, --outfile", Options["--outfile"]) 855 856 if not re.match("^None$", Options["--alignmentSMARTS"], re.I): 857 PatternMol = Chem.MolFromSmarts(Options["--alignmentSMARTS"]) 858 if PatternMol is None: 859 MiscUtil.PrintError("The value specified, %s, using option \"--alignmentSMARTS\" is not a valid SMARTS: Failed to create pattern molecule" % Options["--alignmentSMARTS"]) 860 861 MiscUtil.ValidateOptionIntegerValue("--atomLabelFontSize", Options["--atomLabelFontSize"], {">": 0}) 862 MiscUtil.ValidateOptionFloatValue("-b, --bondLineWidth", Options["--bondLineWidth"], {">": 0.0}) 863 864 MiscUtil.ValidateOptionTextValue("--compute2DCoords", Options["--compute2DCoords"], "yes no auto") 865 866 MiscUtil.ValidateOptionTextValue("--counterCol", Options["--counterCol"], "yes no") 867 MiscUtil.ValidateOptionTextValue("--colVisibility", Options["--colVisibility"], "yes no") 868 869 MiscUtil.ValidateOptionTextValue("--f, -fontBold", Options["--fontBold"], "yes no") 870 871 if not re.match("^None$", Options["--highlightSMARTS"], re.I): 872 PatternMol = Chem.MolFromSmarts(Options["--highlightSMARTS"]) 873 if PatternMol is None: 874 MiscUtil.PrintError("The value specified, %s, using option \"--highlightSMARTS\" is not a valid SMARTS: Failed to create pattern molecule" % Options["--highlightSMARTS"]) 875 876 MiscUtil.ValidateOptionTextValue("--kekulize", Options["--kekulize"], "yes no") 877 878 MiscUtil.ValidateOptionTextValue("-k, --keysNavigation", Options["--keysNavigation"], "yes no") 879 880 MiscUtil.ValidateOptionNumberValues("-m, --molImageSize", Options["--molImageSize"], 2, ",", "integer", {">": 0}) 881 MiscUtil.ValidateOptionTextValue("--molImageEncoded", Options["--molImageEncoded"], "yes no") 882 883 MiscUtil.ValidateOptionIntegerValue("--numOfMolsPerRow", Options["--numOfMolsPerRow"], {">": 0}) 884 885 MiscUtil.ValidateOptionTextValue("-p, --paging", Options["--paging"], "yes no") 886 MiscUtil.ValidateOptionIntegerValue("--pageLength", Options["--pageLength"], {">": 0}) 887 888 MiscUtil.ValidateOptionTextValue("--popover", Options["--popover"], "yes no") 889 MiscUtil.ValidateOptionIntegerValue("--popoverDataCount", Options["--popoverDataCount"], {">": 0}) 890 MiscUtil.ValidateOptionIntegerValue("--popoverTextWidth", Options["--popoverTextWidth"], {">": 0}) 891 892 MiscUtil.ValidateOptionTextValue("--showMolName", Options["--showMolName"], "yes no") 893 894 MiscUtil.ValidateOptionTextValue("--scrollX", Options["--scrollX"], "yes no") 895 MiscUtil.ValidateOptionTextValue("--scrollY", Options["--scrollY"], "yes no") 896 if not re.search("vh$", Options["--scrollYSize"], re.I): 897 MiscUtil.ValidateOptionIntegerValue("--scrollYSize", Options["--scrollYSize"], {">": 0}) 898 899 MiscUtil.ValidateOptionTextValue("--tableFooter", Options["--tableFooter"], "yes no") 900 MiscUtil.ValidateOptionTextValue("--tableHeader", Options["--tableHeader"], "yes no") 901 902 # Setup a usage string for docopt... 903 _docoptUsage_ = """ 904 RDKitDrawMolecules.py - Draw molecules and generate an image or HTML file 905 906 Usage: 907 RDKitDrawMolecules.py [--alignmentSMARTS <SMARTS>] [--atomLabelFontSize <number>] 908 [--bondLineWidth <number>] [--compute2DCoords <yes | no>] [--counterCol <yes or no>] 909 [--colVisibility <yes or no>] [--fontBold <yes or no>] [--footer <text>] [--footerClass <text>] 910 [--header <text>] [--headerStyle <text>] [--highlightSMARTS <SMARTS>] 911 [--infileParams <Name,Value,...>] [--kekulize <yes or no>] [--keysNavigation <yes or no>] 912 [--molImageSize <width,height>] [--molImageEncoded <yes or no> ] 913 [--numOfMolsPerRow <number>] [--overwrite] [--paging <yes or no>] 914 [--pagingType <numbers, simple, ...>] [--pageLength <number>] 915 [--popover <yes or no>] [--popoverDataCount <number>] [--popoverTextWidth <number>] 916 [--showMolName <yes or no>] [--scrollX <yes or no>] [--scrollY <yes or no>] 917 [--scrollYSize <number>] [--tableFooter <yes or no>] [--tableHeader <yes or no>] 918 [--tableHeaderStyle <thead-dark,thead-light,...>] 919 [--tableStyle <table,table-striped,...>] [-w <dir>] -i <infile> -o <outfile> 920 RDKitDrawMolecules.py -h | --help | -e | --examples 921 922 Description: 923 Draw molecules in a grid and write them out as an image file or a HTML table file. The 924 SVG image or HTML table file appears to be the best among all the available image file 925 options, as rendered in a browser. The Python modules aggdraw/cairo are required to 926 generate high quality PNG images. 927 928 The drawing of the molecules are embedded in HTML table columns as in line SVG 929 images. The HTML table is an interactive table and requires internet access for viewing 930 in a browser. It employs he following frameworks: JQuery, Bootstrap, and DataTable. 931 932 The options '--atomLabelFontSize' and '--bondLineWidth' don't appear to work 933 during the generation of a SVG image. In addition, these may not work for other 934 image types in the latest version of RDKIT. 935 936 The supported input file formats are: Mol (.mol), SD (.sdf, .sd), SMILES (.smi, 937 .txt, .csv, .tsv) 938 939 The output image file can be saved in any format supported by the Python Image 940 Library (PIL). The image format is automatically detected from the output file extension. 941 942 Some of the most common output image file formats are: GIF (.gif), JPEG (.jpg), 943 PNG (.png), SVG (.svg), TIFF (.tif). In addition, a HTML (.html) file format 944 containing a table is supported. 945 946 Options: 947 -a, --alignmentSMARTS <SMARTS> [default: none] 948 SMARTS pattern for aligning molecules to a common template. 949 --atomLabelFontSize <number> [default: 12] 950 Font size for drawing atom labels. This option is ignored during the generation of 951 a SVG and HTML output file. This option may not work in the latest version of RDKit. 952 -b, --bondLineWidth <number> [default: 1.2] 953 Line width for drawing bonds. This option is ignored during the generation of a SVG 954 and HTML output file. This option may not work in the latest version of RDKit. 955 -c, --compute2DCoords <yes or no> [default: auto] 956 Compute 2D coordinates of molecules before drawing. Default: yes for all file 957 formats. 958 --counterCol <yes or no> [default: yes] 959 Show a counter column as the first column in the table. It contains the position 960 for each row in the HTML table. This option is only used during the generation of 961 a HTML table file. 962 --colVisibility <yes or no> [default: yes] 963 Show a dropdown button to toggle visibility of columns in the table. This option is 964 only used during the generation of a HTML table file. 965 -e, --examples 966 Print examples. 967 -f --fontBold <yes or no> [default: yes] 968 Make all text fonts bold during the generation of a SVG and HTML output file. This 969 option is ignored for all other output files. This option may not work in the latest 970 version of RDKit. 971 --footer <text> [default: none] 972 Footer text to insert at the bottom of the HTML page after the table. This option is 973 only used during the generation of a HTML table file. 974 --footerClass <text> [default: small text-center text-muted] 975 Footer class style to use with <p> tag. This option is only used during the 976 generation of a HTML table file. 977 -h, --help 978 Print this help message. 979 --header <text> [default: none] 980 Header text to insert at the top of the HTML page before the table. This option is 981 only used during the generation of a HTML table file. 982 --headerStyle <text> [default: h5] 983 Header style to use. Possible values: h1 to h6. This option is only used during the 984 generation of a HTML table file. 985 --highlightSMARTS <SMARTS> [default: none] 986 SMARTS pattern for highlighting atoms and bonds in molecules. All matched 987 substructures are highlighted. 988 -i, --infile <infile> 989 Input file name. 990 --infileParams <Name,Value,...> [default: auto] 991 A comma delimited list of parameter name and value pairs for reading 992 molecules from files. The supported parameter names for different file 993 formats, along with their default values, are shown below: 994 995 SD, MOL: removeHydrogens,yes,sanitize,yes,strictParsing,yes 996 SMILES: smilesColumn,1,smilesNameColumn,2,smilesDelimiter,space, 997 smilesTitleLine,auto,sanitize,yes 998 999 Possible values for smilesDelimiter: space, comma or tab. 1000 -k, --kekulize <yes or no> [default: yes] 1001 Perform kekulization on molecules. This option is ignored during the generation of 1002 a SVG and HTML output file. 1003 --keysNavigation <yes or no> [default: yes] 1004 Provide Excel like keyboard cell navigation for the table. This option is only used 1005 during the generation of a HTML table file. 1006 -m, --molImageSize <width,height> [default: 250,200] 1007 Image size of a molecule in pixels. 1008 --molImageEncoded <yes or no> [default: yes] 1009 Base64 encode SVG image of a molecule for inline embedding in a HTML page. 1010 The inline SVG image may fail to display in browsers without encoding. 1011 -n, --numOfMolsPerRow <number> [default: 4] 1012 Number of molecules to draw in a row. 1013 -o, --outfile <outfile> 1014 Output file name. 1015 --overwrite 1016 Overwrite existing files. 1017 -p, --paging <yes or no> [default: yes] 1018 Provide page navigation for browsing data in the table. This option is only used 1019 during the generation of a HTML table file. 1020 --pagingType <numbers, simple, ...> [default: full_numbers] 1021 Type of page navigation. Possible values: numbers, simple, simple_numbers, 1022 full, full_numbers, or first_last_numbers. 1023 1024 numbers - Page number buttons only 1025 simple - 'Previous' and 'Next' buttons only 1026 simple_numbers - 'Previous' and 'Next' buttons, plus page numbers 1027 full - 'First', 'Previous', 'Next' and 'Last' buttons 1028 full_numbers - 'First', 'Previous', 'Next' and 'Last' buttons, plus 1029 page numbers 1030 first_last_numbers - 'First' and 'Last' buttons, plus page numbers 1031 1032 This option is only used during the generation of a HTML table file. 1033 --pageLength <number> [default: 5] 1034 Number of rows to show per page. This option is only used during the 1035 generation of a HTML table file. 1036 --popover <yes or no> [default: yes] 1037 Display a popover window containing additional information about the 1038 molecule. The popover is opened after a click on the drawing of a 1039 molecule. A subsequent click on the same drawing closes the popover. 1040 This option is only used during the generation of a HTML table file. 1041 --popoverDataCount <number> [default: 25] 1042 Maximum number of data fields to show in a popover window. This option is 1043 only used during the generation of a HTML table file. 1044 --popoverTextWidth <number> [default: 50] 1045 Maximum width in characters for text display in a popover window before 1046 truncating the text. This option is only used during the generation of a HTML 1047 table file. 1048 -s, --showMolName <yes or no> [default: yes] 1049 Show molecule names under the images.This option is only used during the 1050 generation of a HTML table file. 1051 --scrollX <yes or no> [default: yes] 1052 Provide horizontal scroll bar in the table as needed.This option is only used 1053 during the generation of a HTML table file. 1054 --scrollY <yes or no> [default: yes] 1055 Provide vertical scroll bar in the table as needed.This option is only used during 1056 the generation of a HTML table file. 1057 --scrollYSize <number> [default: 75vh] 1058 Maximum height of table viewport either in pixels or percentage of the browser 1059 window height before providing a vertical scroll bar. Default: 75% of the height of 1060 browser window.This option is only used during the generation of a HTML table file. 1061 -t, --tableStyle <table,table-striped,...> [default: table,table-hover,table-sm] 1062 Style of table. Possible values: table, table-striped, table-bordered, 1063 table-hover, table-dark, table-sm, none, or All. Default: 'table,table-hover'. A 1064 comma delimited list of any valid Bootstrap table styles is also supported 1065 1066 This option is only used during the generation of a HTML table file. 1067 --tableFooter <yes or no> [default: yes] 1068 Show Excel style column headers at the end of the table. This option is only 1069 used during the generation of a HTML table file. 1070 --tableHeader <yes or no> [default: yes] 1071 Show Excel style column headers in the table. This option is only used 1072 during the generation of a HTML table file. 1073 --tableHeaderStyle <thead-dark,thead-light,...> [default: thead-dark] 1074 Style of table header. Possible values: thead-dark, thead-light, or none. 1075 The names of the following contextual color classes are also supported: 1076 table-primary (Blue), table-success (Green), table-danger (Red), table-info 1077 (Light blue), table-warning (Orange), table-active (Grey), table-light (Light 1078 grey), and table-dark (Dark grey). 1079 1080 This option is only used during the generation of a HTML table file. 1081 -w, --workingdir <dir> 1082 Location of working directory which defaults to the current directory. 1083 1084 Examples: 1085 To automatically compute 2D coordinates for molecules in a SMILES file and 1086 generate a SVG image file containing 4 molecules per row in a grid with cell 1087 size of 250 x 200 pixels, type: 1088 1089 % RDKitDrawMolecules.py -i Sample.smi -o SampleOut.svg 1090 1091 To automatically compute 2D coordinates for molecules in a SMILES file and 1092 generate a SVG image file containing 2 molecules per row in a grid with cell 1093 size of 400 x 300 pixels and without any keulization along with highlighting 1094 a specific set of atoms and bonds indicated by a SMARTS pattern, type: 1095 1096 % RDKitDrawMolecules.py -n 2 -m "400,300" -k no --fontBold no 1097 --highlightSMARTS 'c1ccccc1' -i Sample.smi -o SampleOut.svg 1098 1099 To generate a PNG image file for molecules in a SD file using existing 2D 1100 coordinates, type 1101 1102 % RDKitDrawMolecules.py --compute2DCoords no -i Sample.sdf 1103 -o SampleOut.png 1104 1105 To automatically compute 2D coordinates for molecules in a SD file and 1106 generate a HTML file containing 4 molecules per row in a table, along with 1107 all the bells and whistles to interact with the table, type: 1108 1109 % RDKitDrawMolecules.py -i Sample.sdf -o SampleOut.html 1110 1111 To automatically compute 2D coordinates for molecules in a SD file and 1112 generate a HTML file containing 4 molecules per row in a table without 1113 any bells and whistles to interact with the table, type: 1114 1115 % RDKitDrawMolecules.py --counterCol no --colVisibility no 1116 --keysNavigation no --paging no --popover no --scrollX no 1117 --scrollY no --tableFooter no --tableHeader no -i Sample.sdf 1118 -o SampleOut.html 1119 1120 To automatically compute 2D coordinates for molecules in a CSV SMILES file 1121 with column headers, SMILES strings in column 1, and name in column 2 and 1122 generate a PDF image file, type: 1123 1124 % RDKitDrawMolecules.py --infileParams "smilesDelimiter,comma, 1125 smilesTitleLine,yes,smilesColumn,1,smilesNameColumn,2" 1126 -i SampleSMILES.csv -o SampleOut.pdf 1127 1128 Author: 1129 Manish Sud(msud@san.rr.com) 1130 1131 See also: 1132 RDKitConvertFileFormat.py, RDKitDrawMoleculesAndDataTable.py, RDKitRemoveDuplicateMolecules.py, 1133 RDKitSearchFunctionalGroups.py, RDKitSearchSMARTS.py 1134 1135 Copyright: 1136 Copyright (C) 2025 Manish Sud. All rights reserved. 1137 1138 The functionality available in this script is implemented using RDKit, an 1139 open source toolkit for cheminformatics developed by Greg Landrum. 1140 1141 This file is part of MayaChemTools. 1142 1143 MayaChemTools is free software; you can redistribute it and/or modify it under 1144 the terms of the GNU Lesser General Public License as published by the Free 1145 Software Foundation; either version 3 of the License, or (at your option) any 1146 later version. 1147 1148 """ 1149 1150 if __name__ == "__main__": 1151 main()