diff --git a/rdeditor/molViewWidget.py b/rdeditor/molViewWidget.py index 8cd606f..651b447 100644 --- a/rdeditor/molViewWidget.py +++ b/rdeditor/molViewWidget.py @@ -21,7 +21,7 @@ # The Viewer Class class MolWidget(QtSvgWidgets.QSvgWidget): - def __init__(self, mol=None, parent=None): + def __init__(self, mol=None, parent=None, moldrawoptions: rdMolDraw2D.MolDrawOptions = None): # Also init the super class super(MolWidget, self).__init__(parent) @@ -49,6 +49,16 @@ def __init__(self, mol=None, parent=None): self._sanitize = False self._updatepropertycache = False + # Draw options + if moldrawoptions is None: + self._moldrawoptions = rdMolDraw2D.MolDraw2DSVG(300, 300).drawOptions() + self._moldrawoptions.prepareMolsBeforeDrawing = True + self._moldrawoptions.addStereoAnnotation = True + self._moldrawoptions.unspecifiedStereoIsUnknown = False + self._moldrawoptions.fixedBondLength = 25 + else: + self._moldrawoptions = moldrawoptions + # Bind signales to slots for automatic actions self.molChanged.connect(self.sanitize_draw) self.selectionChanged.connect(self.draw) @@ -76,6 +86,25 @@ def darkmode(self, value: bool): self._darkmode = bool(value) self.draw() + @property + def moldrawoptions(self): + """Returns the current drawing options. + If settings aremanipulated directly, a drawSettingsChanged signal is not emitted, + consider using setDrawOption instead.""" + return self._moldrawoptions + + @moldrawoptions.setter + def moldrawoptions(self, value): + self._moldrawoptions = value + self.drawSettingsChanged.emit() + + def getDrawOption(self, attribute): + return getattr(self._moldrawoptions, attribute) + + def setDrawOption(self, attribute, value): + setattr(self._moldrawoptions, attribute, value) + self.drawSettingsChanged.emit() + # Getter and setter for mol molChanged = QtCore.Signal(name="molChanged") @@ -309,13 +338,15 @@ def getMolSvg(self): if self._drawmol is not None: # Chiral tags on R/S # chiraltags = Chem.FindMolChiralCenters(self._drawmol) + self.drawer.SetDrawOptions(self._moldrawoptions) opts = self.drawer.drawOptions() if self._darkmode: rdMolDraw2D.SetDarkMode(opts) if (not self.molecule_sanitizable) and self.unsanitizable_background_colour: opts.setBackgroundColour(self.unsanitizable_background_colour) - opts.prepareMolsBeforeDrawing = False - opts.addStereoAnnotation = True # Show R/S and E/Z + # opts.prepareMolsBeforeDrawing = True + # opts.addStereoAnnotation = True # Show R/S and E/Z + # opts.unspecifiedStereoIsUnknown = True # Show wiggly bond at undefined stereo centre # for tag in chiraltags: # idx = tag[0] # opts.atomLabels[idx] = self._drawmol.GetAtomWithIdx(idx).GetSymbol() + ":" + tag[1] diff --git a/rdeditor/rdEditor.py b/rdeditor/rdEditor.py index dd6f895..f565a2b 100644 --- a/rdeditor/rdEditor.py +++ b/rdeditor/rdEditor.py @@ -33,6 +33,23 @@ def __init__(self, fileName=None, loglevel="WARNING"): [os.path.abspath(os.path.dirname(__file__)) + "/icon_themes/"] ) self.loglevels = ["Critical", "Error", "Warning", "Info", "Debug", "Notset"] + # RDKit draw options, tooltip, default value is read from molViewWidget + self._drawopts_actions = [ + ( + "prepareMolsBeforeDrawing", + "Prepare molecules before drawing (i.e. fix stereochemistry and annotations)", + ), + ( + "addStereoAnnotation", + "Add stereo annotation (R/S and E/Z)", + ), + ( + "unspecifiedStereoIsUnknown", + "Show wiggly bond at potential undefined chiral stereo centres " + + "and cross bonds for undefined doublebonds", + ), + ] + self.editor = MolEditWidget() self.chemEntityActionGroup = QtGui.QActionGroup(self, exclusive=True) self.ptable = PTable(self.chemEntityActionGroup) @@ -133,6 +150,18 @@ def applySettings(self): self.editor.kekulize_on_cleanup = kekulize_on_cleanup self.cleanupSettingActions["kekulize_on_cleanup"].setChecked(kekulize_on_cleanup) + # Draw options + for key, statusTip in self._drawopts_actions: + viewer_value = self.editor.getDrawOption(key) + settings_value = self.settings.value(f"drawoptions/{key}", viewer_value, type=bool) + if settings_value != viewer_value: + self.editor.setDrawOption(key, settings_value) + self.drawOptionsActions[key].setChecked(settings_value) + + if self.settings.contains("drawoptions/fixedBondLength"): + fixedBondLength = self.settings.value("drawoptions/fixedBondLength", 15, type=int) + self.editor.setDrawOption("fixedBondLength", fixedBondLength) + # Function to setup status bar, central widget, menu bar, tool bar def SetupComponents(self): self.myStatusBar = QStatusBar() @@ -218,6 +247,9 @@ def CreateMenus(self): self.cleanupMenu = self.settingsMenu.addMenu("Cleanup") for key, action in self.cleanupSettingActions.items(): self.cleanupMenu.addAction(action) + self.drawOptionsMenu = self.settingsMenu.addMenu("Drawing Options") + for key, statusTip in self._drawopts_actions: + self.drawOptionsMenu.addAction(self.drawOptionsActions[key]) # Help menu self.helpMenu.addAction(self.aboutAction) @@ -452,6 +484,13 @@ def setLogLevel(self): self.settings.setValue("loglevel", loglevel) self.settings.sync() + def setDrawOption(self): + sender = self.sender() + option = sender.objectName() + self.editor.setDrawOption(option, sender.isChecked()) + self.settings.setValue(f"drawoptions/{option}", sender.isChecked()) + self.settings.sync() + def setTheme(self): sender = self.sender() theme_name = sender.objectName() @@ -914,6 +953,13 @@ def CreateActions(self): ) self.loglevelActionGroup.addAction(self.loglevelactions[key]) + self.drawOptionsActions = {} + for key, statusTip in self._drawopts_actions: + self.drawOptionsActions[key] = QAction( + key, self, statusTip=statusTip, triggered=self.setDrawOption, objectName=key, checkable=True + ) + # self.drawOptionsActionGroup.addAction(self.drawOptionsActions[key]) + self.openChemRxiv = QAction( QIcon.fromTheme("icons8-Exit"), "ChemRxiv Preprint",