From a98b434e9fc569319d76b2f0a24bce6fbc7521bb Mon Sep 17 00:00:00 2001 From: Jack Day Date: Sun, 8 Dec 2024 20:32:28 +1000 Subject: [PATCH 1/7] When recalculating coordinates some of the stereo chem is not updated inside the mol. Setting prepareMolsBeforeDrawing seems to fix this --- rdeditor/molViewWidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rdeditor/molViewWidget.py b/rdeditor/molViewWidget.py index 8cd606f..7e608f0 100644 --- a/rdeditor/molViewWidget.py +++ b/rdeditor/molViewWidget.py @@ -314,7 +314,7 @@ def getMolSvg(self): rdMolDraw2D.SetDarkMode(opts) if (not self.molecule_sanitizable) and self.unsanitizable_background_colour: opts.setBackgroundColour(self.unsanitizable_background_colour) - opts.prepareMolsBeforeDrawing = False + opts.prepareMolsBeforeDrawing = True opts.addStereoAnnotation = True # Show R/S and E/Z # for tag in chiraltags: # idx = tag[0] From 46287744fd2835e4ddf80d5afdeff63dbfc5befd Mon Sep 17 00:00:00 2001 From: Jack Day Date: Wed, 11 Dec 2024 07:38:28 +1000 Subject: [PATCH 2/7] Show wiggly bond at undefined stereo centre --- rdeditor/molViewWidget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rdeditor/molViewWidget.py b/rdeditor/molViewWidget.py index 8cd606f..2cb66ae 100644 --- a/rdeditor/molViewWidget.py +++ b/rdeditor/molViewWidget.py @@ -316,6 +316,7 @@ def getMolSvg(self): opts.setBackgroundColour(self.unsanitizable_background_colour) opts.prepareMolsBeforeDrawing = False 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] From 151892cf952ee9933ab58241835aad3dfc5d8842 Mon Sep 17 00:00:00 2001 From: Esben Jannik Bjerrum Date: Mon, 30 Dec 2024 15:27:32 +0100 Subject: [PATCH 3/7] custom drawing options exposed and managed on molViewWidget --- rdeditor/molViewWidget.py | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/rdeditor/molViewWidget.py b/rdeditor/molViewWidget.py index 214a115..53d7d6d 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,15 @@ 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 + else: + self._moldrawoptions = moldrawoptions + # Bind signales to slots for automatic actions self.molChanged.connect(self.sanitize_draw) self.selectionChanged.connect(self.draw) @@ -76,6 +85,25 @@ def darkmode(self, value: bool): self._darkmode = bool(value) self.draw() + @property + def drawOptions(self): + """Returns the current drawing options. + If settings aremanipulated directly, a drawSettingsChanged signal is not emitted, + consider using setDrawOption instead.""" + return self._moldrawoptions + + @drawOptions.setter + def drawOptions(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,14 +337,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 = True - opts.addStereoAnnotation = True # Show R/S and E/Z - opts.unspecifiedStereoIsUnknown = True # Show wiggly bond at undefined stereo centre + # 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] From a3efdfebe00e9863fd007d4c115fac66316314c2 Mon Sep 17 00:00:00 2001 From: Esben Jannik Bjerrum Date: Mon, 30 Dec 2024 16:27:36 +0100 Subject: [PATCH 4/7] Added a settings menu that manipulates the drawing options of the molViewWidget. Not yet bound to persistent settings. --- rdeditor/molViewWidget.py | 6 +++--- rdeditor/rdEditor.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/rdeditor/molViewWidget.py b/rdeditor/molViewWidget.py index 53d7d6d..cdf147d 100644 --- a/rdeditor/molViewWidget.py +++ b/rdeditor/molViewWidget.py @@ -86,14 +86,14 @@ def darkmode(self, value: bool): self.draw() @property - def drawOptions(self): + 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 - @drawOptions.setter - def drawOptions(self, value): + @moldrawoptions.setter + def moldrawoptions(self, value): self._moldrawoptions = value self.drawSettingsChanged.emit() diff --git a/rdeditor/rdEditor.py b/rdeditor/rdEditor.py index dd6f895..ae5f07a 100644 --- a/rdeditor/rdEditor.py +++ b/rdeditor/rdEditor.py @@ -33,6 +33,20 @@ def __init__(self, fileName=None, loglevel="WARNING"): [os.path.abspath(os.path.dirname(__file__)) + "/icon_themes/"] ) self.loglevels = ["Critical", "Error", "Warning", "Info", "Debug", "Notset"] + self._drawopts_actions = [ + ( + "prepareMolsBeforeDrawing", + True, + "Prepare molecules before drawing (i.e. fix stereochemistry and annotations)", + ), + ("addStereoAnnotation", True, "Add stereo annotation (R/S and E/Z)"), + ( + "unspecifiedStereoIsUnknown", + False, + "Show wiggly bond at potentialundefined chiral stereo centres and cross bonds for undefined doublebonds", + ), + ] + self.editor = MolEditWidget() self.chemEntityActionGroup = QtGui.QActionGroup(self, exclusive=True) self.ptable = PTable(self.chemEntityActionGroup) @@ -218,6 +232,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, value, statusTip in self._drawopts_actions: + self.drawOptionsMenu.addAction(self.drawOptionsActions[key]) # Help menu self.helpMenu.addAction(self.aboutAction) @@ -452,6 +469,11 @@ 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()) + def setTheme(self): sender = self.sender() theme_name = sender.objectName() @@ -914,6 +936,13 @@ def CreateActions(self): ) self.loglevelActionGroup.addAction(self.loglevelactions[key]) + self.drawOptionsActions = {} + for key, value, 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", From dd8049b3bc6bd318ff5951f72840a93c7700486a Mon Sep 17 00:00:00 2001 From: Esben Jannik Bjerrum Date: Mon, 30 Dec 2024 17:28:39 +0100 Subject: [PATCH 5/7] Added synchronization between setting and draw options. --- rdeditor/rdEditor.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/rdeditor/rdEditor.py b/rdeditor/rdEditor.py index ae5f07a..6e20942 100644 --- a/rdeditor/rdEditor.py +++ b/rdeditor/rdEditor.py @@ -33,16 +33,18 @@ 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", - True, "Prepare molecules before drawing (i.e. fix stereochemistry and annotations)", ), - ("addStereoAnnotation", True, "Add stereo annotation (R/S and E/Z)"), + ( + "addStereoAnnotation", + "Add stereo annotation (R/S and E/Z)", + ), ( "unspecifiedStereoIsUnknown", - False, "Show wiggly bond at potentialundefined chiral stereo centres and cross bonds for undefined doublebonds", ), ] @@ -147,6 +149,14 @@ def applySettings(self): self.editor.kekulize_on_cleanup = kekulize_on_cleanup self.cleanupSettingActions["kekulize_on_cleanup"].setChecked(kekulize_on_cleanup) + # Draw options + for key, value, 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) + # Function to setup status bar, central widget, menu bar, tool bar def SetupComponents(self): self.myStatusBar = QStatusBar() @@ -473,6 +483,8 @@ 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() From ae17dd42f7b40d4c1fba2e8dd557ff1570bbcc7f Mon Sep 17 00:00:00 2001 From: Esben Jannik Bjerrum Date: Mon, 30 Dec 2024 17:38:40 +0100 Subject: [PATCH 6/7] Fixed formatting and bugs --- rdeditor/rdEditor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rdeditor/rdEditor.py b/rdeditor/rdEditor.py index 6e20942..615f29a 100644 --- a/rdeditor/rdEditor.py +++ b/rdeditor/rdEditor.py @@ -45,7 +45,8 @@ def __init__(self, fileName=None, loglevel="WARNING"): ), ( "unspecifiedStereoIsUnknown", - "Show wiggly bond at potentialundefined chiral stereo centres and cross bonds for undefined doublebonds", + "Show wiggly bond at potential undefined chiral stereo centres " + + "and cross bonds for undefined doublebonds", ), ] @@ -150,7 +151,7 @@ def applySettings(self): self.cleanupSettingActions["kekulize_on_cleanup"].setChecked(kekulize_on_cleanup) # Draw options - for key, value, statusTip in self._drawopts_actions: + 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: @@ -243,7 +244,7 @@ def CreateMenus(self): for key, action in self.cleanupSettingActions.items(): self.cleanupMenu.addAction(action) self.drawOptionsMenu = self.settingsMenu.addMenu("Drawing Options") - for key, value, statusTip in self._drawopts_actions: + for key, statusTip in self._drawopts_actions: self.drawOptionsMenu.addAction(self.drawOptionsActions[key]) # Help menu @@ -949,7 +950,7 @@ def CreateActions(self): self.loglevelActionGroup.addAction(self.loglevelactions[key]) self.drawOptionsActions = {} - for key, value, statusTip in self._drawopts_actions: + for key, statusTip in self._drawopts_actions: self.drawOptionsActions[key] = QAction( key, self, statusTip=statusTip, triggered=self.setDrawOption, objectName=key, checkable=True ) From 7f5bbc610d5a21f2edec22c39623ad03c26c9f46 Mon Sep 17 00:00:00 2001 From: Esben Jannik Bjerrum Date: Mon, 30 Dec 2024 21:13:28 +0100 Subject: [PATCH 7/7] Setting moldrawoptions.fixedBondLength to something else in the rdeditor.conf file is acknowledged. Otherwise defined in molViewWidget --- rdeditor/molViewWidget.py | 1 + rdeditor/rdEditor.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/rdeditor/molViewWidget.py b/rdeditor/molViewWidget.py index cdf147d..651b447 100644 --- a/rdeditor/molViewWidget.py +++ b/rdeditor/molViewWidget.py @@ -55,6 +55,7 @@ def __init__(self, mol=None, parent=None, moldrawoptions: rdMolDraw2D.MolDrawOpt self._moldrawoptions.prepareMolsBeforeDrawing = True self._moldrawoptions.addStereoAnnotation = True self._moldrawoptions.unspecifiedStereoIsUnknown = False + self._moldrawoptions.fixedBondLength = 25 else: self._moldrawoptions = moldrawoptions diff --git a/rdeditor/rdEditor.py b/rdeditor/rdEditor.py index 615f29a..f565a2b 100644 --- a/rdeditor/rdEditor.py +++ b/rdeditor/rdEditor.py @@ -158,6 +158,10 @@ def applySettings(self): 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()