Source code for schrodinger.application.jaguar.gui.theory_selector
"""
Module containing classes for selection of theory in Jaguar GUIs.
Copyright Schrodinger, LLC. All rights reserved.
"""
import csv
from schrodinger.application.jaguar.gui.utils import THEORY_HF
from schrodinger.application.jaguar.gui.utils import THEORY_LMP2
from schrodinger.application.jaguar.gui.utils import THEORY_RIMP2
from schrodinger.application.jaguar.jaguar_keyword_utils import LEVELS_OF_THEORY
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt
from schrodinger.ui.qt import filter_list
from schrodinger.ui.qt import pop_up_widgets
from schrodinger.utils import csv_unicode
NON_DFT_POSTFIX = " (non-DFT)"
DOCS_FIELDS = ['description', 'references']
[docs]class TheoryListWidgetItem(QtWidgets.QListWidgetItem):
"""
Custom list widget items for theory selection.
"""
[docs] def __init__(self,
text,
method=None,
is_recommended=False,
is_dispersion_corrected_dft=False,
is_long_range_corrected_dft=False,
is_hybrid_dft=False,
is_meta_gga_dft=False,
is_gga_dft=False,
is_lda_dft=False,
is_non_dft=False):
"""
:param text: Display text for this list item
:type text: str
:param method: Actual method name for this list item.
:type method: str
:param is_recommended: Whether this list item is recommended
:type is_recommended: bool
:param is_dispersion_corrected_dft: Whether this list item is dispersion-corrected DFT
:type is_dispersion_corrected_dft: bool
:param is_long_range_corrected_dft: Whether this list item is long-range corrected DFT
:type is_long_range_corrected_dft: bool
:param is_hybrid_dft: Whether this list item is hybrid DFT
:type is_hybrid_dft: bool
:param is_meta_gga_dft: Whether this list item is meta-GGA DFT
:type is_meta_gga_dft: bool
:param is_gga_dft: Whether this list item is GGA DFT
:type is_gga_dft: bool
:param is_lda_dft: Whether this list item is LDA DFT
:type is_lda_dft: bool
:param is_non_dft: Whether this list item is non-DFT
:type is_non_dft: bool
"""
super().__init__(text)
self.method = method
self.is_recommended = is_recommended
self.is_dispersion_corrected_dft = is_dispersion_corrected_dft
self.is_long_range_corrected_dft = is_long_range_corrected_dft
self.is_hybrid_dft = is_hybrid_dft
self.is_meta_gga_dft = is_meta_gga_dft
self.is_gga_dft = is_gga_dft
self.is_lda_dft = is_lda_dft
self.is_non_dft = is_non_dft
[docs]class TheorySelectorFilterListPopUp(filter_list.FilterListPopUp):
"""
Class allowing for dynamic filtering and selection of methods.
:cvar DFT_ONLY: Whether only DFT functionals should be included
:type DFT_ONLY: bool
"""
DFT_ONLY = False
[docs] def __init__(self, parent):
list_items = self._getListItems()
cbs = self._getFilterCheckBoxes()
super().__init__(parent, list_items, cbs,
'Limit list to matching methods')
def _getListItems(self):
"""
:return: A tuple of the list items for the list widget.
:rtype: tuple(TheoryListWidgetItem)
"""
list_items = []
with csv_unicode.reader_open(LEVELS_OF_THEORY) as fh:
reader = csv.DictReader(fh)
for row in reader:
for col in DOCS_FIELDS:
del row[col]
for col, val in row.items():
if col != 'method':
if val not in ("0", "1"):
raise RuntimeError(
f"Unexpected {col} value for {row['method']}: {val}"
)
row[col] = val == '1'
is_non_dft = row['is_non_dft']
method = row['method']
if is_non_dft and self.DFT_ONLY:
continue
disp_name = method
if is_non_dft:
disp_name = disp_name + NON_DFT_POSTFIX
list_items.append(TheoryListWidgetItem(disp_name, **row))
return tuple(list_items)
def _getFilterCheckBoxes(self):
"""
:return: a tuple of the filter checkboxes for this list
:rtype: tuple(filter_list.FilterCheckBox)
"""
recommended_func = lambda li: li.is_recommended
recommended_cb = filter_list.FilterCheckBox("Recommended",
recommended_func)
disp_corrected_func = lambda li: li.is_dispersion_corrected_dft
disp_corrected_cb = filter_list.FilterCheckBox(
'Dispersion corrected DFT', disp_corrected_func)
long_range_func = lambda li: li.is_long_range_corrected_dft
long_range_cb = filter_list.FilterCheckBox('Long range corrected DFT',
long_range_func)
hybrid_func = lambda li: li.is_hybrid_dft
hybrid_cb = filter_list.FilterCheckBox("Hybrid DFT", hybrid_func)
meta_gga_func = lambda li: li.is_meta_gga_dft
meta_gga_cb = filter_list.FilterCheckBox('Meta GGA DFT', meta_gga_func)
gga_func = lambda li: li.is_gga_dft
gga_cb = filter_list.FilterCheckBox('GGA DFT', gga_func)
lda_func = lambda li: li.is_lda_dft
lda_cb = filter_list.FilterCheckBox("LDA DFT", lda_func)
non_dft_func = lambda li: li.is_non_dft
non_dft_cb = filter_list.FilterCheckBox("Non-DFT", non_dft_func)
cbs = [
recommended_cb, disp_corrected_cb, long_range_cb, hybrid_cb,
meta_gga_cb, gga_cb, lda_cb
]
if not self.DFT_ONLY:
cbs += [non_dft_cb]
return tuple(cbs)
def _getListItemTextForTheory(self, theory):
"""
:return: The list item text that is shown for a specified theory method.
:rtype: str
"""
if theory in [THEORY_HF, THEORY_LMP2, THEORY_RIMP2]:
theory_text = theory + NON_DFT_POSTFIX
else:
theory_text = theory
return theory_text
[docs] def setMethod(self, theory=None):
"""
Set the theory to the specified value.
:param theory: Theory value to set
:type theory: str or None
:return: True if theory was set, False otherwise.
:rtype: bool
"""
if theory is None:
self._list_widget.setCurrentItem(None)
return
theory = self._getListItemTextForTheory(theory)
items = self._list_widget.findItems(theory,
Qt.MatchFlag.MatchFixedString)
if len(items) != 1:
return False
item = items[0]
if item.isHidden():
return False
self._list_widget.setCurrentItem(item)
return True
[docs] def getMethod(self):
"""
:return: The currently selected theory value.
:rtype: str or None
"""
current_item = self._list_widget.currentItem()
if not current_item:
return None
return current_item.method
[docs] def addMethod(self, method, category, display_name=None):
"""
Add the specified method to the popup's available methods.
:param method: Method to be added
:type method: str
:param category: Category for this method
:type category: str
:param display_name: Display name for this method. If not
specified, the method value will be used.
:type display_name: str or None
"""
if display_name is None:
display_name = method
list_item = TheoryListWidgetItem(display_name, method, category)
self._list_widget.addItem(list_item)
[docs] def isItemHidden(self, theory):
# See parent class for argument specification
theory = self._getListItemTextForTheory(theory)
return super().isItemHidden(theory)
[docs]class DftTheorySelectorFilterListPopUp(TheorySelectorFilterListPopUp):
"""
Class allowing for dynamic filtering and selection of DFT functionals
"""
DFT_ONLY = True
[docs]class TheorySelectorFilterListToolButton(
filter_list.ToolButtonWithFilterListPopUp):
"""
Custom tool button with a theory selector filter list pop up.
"""
POP_UP_CLASS = TheorySelectorFilterListPopUp
[docs] def getMethod(self):
"""
:return: the currently selected theory
:rtype: str or None
"""
return self._pop_up.getMethod()
[docs] def setMethod(self, theory=None):
"""
Set the current theory value.
:param theory: Theory value to be set
:type theory: str or None
:return: True if the theory level was set, False otherwise.
:rtype: bool
"""
return self._pop_up.setMethod(theory)
[docs] def addMethod(self, method, category, display_name=None):
"""
Add the specified method to the popup's available methods.
:param method: Method to be added
:type method: str
:param category: Category for this method
:type category: str
:param display_name: Display name for this method. If not
specified, the method value will be used.
:type display_name: str or None
"""
self._pop_up.addMethod(method, category, display_name)
[docs] def applySettings(self, settings):
"""
Apply the specified filter settings to the pop up
:param settings: Settings to be applied
:type settings: dict
"""
self._pop_up.applySettings(settings)
[docs]class DftTheorySelectorFilterListToolButton(TheorySelectorFilterListToolButton):
"""
Custom tool button with a theory selector filter list pop up for
DFT functionals.
"""
POP_UP_CLASS = DftTheorySelectorFilterListPopUp
[docs]class FilterTheorySelectorReadOnlyLineEdit(pop_up_widgets.LineEditWithPopUp):
"""
A read-only line edit used as an editor for table models with a TheorySelectorFilterPopUp.
:ivar filtersChanged: Signal emitted when filters are toggled.
emits a dict of current filter settings.
:type filtersChanged: QtCore.pyQtSignal(dict)
"""
filtersChanged = QtCore.pyqtSignal(dict)
[docs] def __init__(self, parent):
super().__init__(parent, TheorySelectorFilterListPopUp)
self.setReadOnly(True)
self._pop_up.filtersChanged.connect(self.filtersChanged)
# FIXME - The below is intended to assist in
# closing the popup when embedded in a table, but
# doesn't seem to be working as expected...
self.setFocusPolicy(Qt.StrongFocus)
[docs] def setMethod(self, theory=None):
"""
Set the current theory value.
:param theory: The current theory value to be set.
:type theory: str or None
"""
self._pop_up.setMethod(theory)
[docs] def popUpUpdated(self):
"""
Update the line edit text based on the current pop up selection.
"""
theory = self._pop_up.getMethod()
if theory is not None:
self.setText(theory)
[docs] def applySettings(self, settings):
"""
Apply the specified filter settings to the pop up.
:param settings: Filter settings to apply
:type settings: dict
"""
self._pop_up.applySettings(settings)