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