summaryrefslogtreecommitdiff
path: root/silx/gui/widgets/PeriodicTable.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/widgets/PeriodicTable.py')
-rw-r--r--silx/gui/widgets/PeriodicTable.py831
1 files changed, 0 insertions, 831 deletions
diff --git a/silx/gui/widgets/PeriodicTable.py b/silx/gui/widgets/PeriodicTable.py
deleted file mode 100644
index 0233e8c..0000000
--- a/silx/gui/widgets/PeriodicTable.py
+++ /dev/null
@@ -1,831 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""Periodic table widgets
-
-Classes
--------
-
-Widgets:
-
- - :class:`PeriodicTable`
- - :class:`PeriodicList`
- - :class:`PeriodicCombo`
-
-Data model:
-
- - :class:`PeriodicTableItem`
- - :class:`ColoredPeriodicTableItem`
-
-
-Example of usage
-----------------
-
-This example uses the widgets with the standard builtin elements list.
-
-.. code-block:: python
-
- from silx.gui import qt
- from silx.gui.widgets.PeriodicTable import PeriodicTable, \
- PeriodicCombo, PeriodicList
-
- a = qt.QApplication([])
-
- w = qt.QTabWidget()
-
- ptable = PeriodicTable(w, selectable=True)
- pcombo = PeriodicCombo(w)
- plist = PeriodicList(w)
-
- w.addTab(ptable, "PeriodicTable")
- w.addTab(plist, "PeriodicList")
- w.addTab(pcombo, "PeriodicCombo")
-
- ptable.setSelection(['H', 'Fe', 'Si'])
- plist.setSelectedElements(['H', 'Be', 'F'])
- pcombo.setSelection("Li")
-
- def change_list(items):
- print("New list selection:", [item.symbol for item in items])
-
- def change_combo(item):
- print("New combo selection:", item.symbol)
-
- def click_table(item):
- print("New table click:", item.symbol)
-
- def change_table(items):
- print("New table selection:", [item.symbol for item in items])
-
- ptable.sigElementClicked.connect(click_table)
- ptable.sigSelectionChanged.connect(change_table)
- plist.sigSelectionChanged.connect(change_list)
- pcombo.sigSelectionChanged.connect(change_combo)
-
- w.show()
- a.exec_()
-
-
-The second example explains how to define custom elements.
-
-.. code-block:: python
-
- from silx.gui import qt
- from silx.gui.widgets.PeriodicTable import PeriodicTable, \
- PeriodicCombo, PeriodicList
- from silx.gui.widgets.PeriodicTable import PeriodicTableItem
-
- # subclass PeriodicTableItem
- class MyPeriodicTableItem(PeriodicTableItem):
- "New item with added mass number and number of protons"
- def __init__(self, symbol, Z, A, col, row, name, mass,
- subcategory=""):
- PeriodicTableItem.__init__(
- self, symbol, Z, col, row, name, mass,
- subcategory)
-
- self.A = A
- "Mass number (neutrons + protons)"
-
- self.num_neutrons = A - Z
- "Number of neutrons"
-
- # build your list of elements
- my_elements = [MyPeriodicTableItem("H", 1, 1, 1, 1, "hydrogen",
- 1.00800, "diatomic nonmetal"),
- MyPeriodicTableItem("He", 2, 4, 18, 1, "helium",
- 4.0030, "noble gas"),
- # etc ...
- ]
-
- app = qt.QApplication([])
-
- ptable = PeriodicTable(elements=my_elements, selectable=True)
- ptable.show()
-
- def click_table(item):
- "Callback function printing the mass number of clicked element"
- print("New table click, mass number:", item.A)
-
- ptable.sigElementClicked.connect(click_table)
- app.exec_()
-
-"""
-
-__authors__ = ["E. Papillon", "V.A. Sole", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "26/01/2017"
-
-from collections import OrderedDict
-import logging
-from silx.gui import qt
-
-_logger = logging.getLogger(__name__)
-
-# Symbol Atomic Number col row name mass subcategory
-_elements = [("H", 1, 1, 1, "hydrogen", 1.00800, "diatomic nonmetal"),
- ("He", 2, 18, 1, "helium", 4.0030, "noble gas"),
- ("Li", 3, 1, 2, "lithium", 6.94000, "alkali metal"),
- ("Be", 4, 2, 2, "beryllium", 9.01200, "alkaline earth metal"),
- ("B", 5, 13, 2, "boron", 10.8110, "metalloid"),
- ("C", 6, 14, 2, "carbon", 12.0100, "polyatomic nonmetal"),
- ("N", 7, 15, 2, "nitrogen", 14.0080, "diatomic nonmetal"),
- ("O", 8, 16, 2, "oxygen", 16.0000, "diatomic nonmetal"),
- ("F", 9, 17, 2, "fluorine", 19.0000, "diatomic nonmetal"),
- ("Ne", 10, 18, 2, "neon", 20.1830, "noble gas"),
- ("Na", 11, 1, 3, "sodium", 22.9970, "alkali metal"),
- ("Mg", 12, 2, 3, "magnesium", 24.3200, "alkaline earth metal"),
- ("Al", 13, 13, 3, "aluminium", 26.9700, "post transition metal"),
- ("Si", 14, 14, 3, "silicon", 28.0860, "metalloid"),
- ("P", 15, 15, 3, "phosphorus", 30.9750, "polyatomic nonmetal"),
- ("S", 16, 16, 3, "sulphur", 32.0660, "polyatomic nonmetal"),
- ("Cl", 17, 17, 3, "chlorine", 35.4570, "diatomic nonmetal"),
- ("Ar", 18, 18, 3, "argon", 39.9440, "noble gas"),
- ("K", 19, 1, 4, "potassium", 39.1020, "alkali metal"),
- ("Ca", 20, 2, 4, "calcium", 40.0800, "alkaline earth metal"),
- ("Sc", 21, 3, 4, "scandium", 44.9600, "transition metal"),
- ("Ti", 22, 4, 4, "titanium", 47.9000, "transition metal"),
- ("V", 23, 5, 4, "vanadium", 50.9420, "transition metal"),
- ("Cr", 24, 6, 4, "chromium", 51.9960, "transition metal"),
- ("Mn", 25, 7, 4, "manganese", 54.9400, "transition metal"),
- ("Fe", 26, 8, 4, "iron", 55.8500, "transition metal"),
- ("Co", 27, 9, 4, "cobalt", 58.9330, "transition metal"),
- ("Ni", 28, 10, 4, "nickel", 58.6900, "transition metal"),
- ("Cu", 29, 11, 4, "copper", 63.5400, "transition metal"),
- ("Zn", 30, 12, 4, "zinc", 65.3800, "transition metal"),
- ("Ga", 31, 13, 4, "gallium", 69.7200, "post transition metal"),
- ("Ge", 32, 14, 4, "germanium", 72.5900, "metalloid"),
- ("As", 33, 15, 4, "arsenic", 74.9200, "metalloid"),
- ("Se", 34, 16, 4, "selenium", 78.9600, "polyatomic nonmetal"),
- ("Br", 35, 17, 4, "bromine", 79.9200, "diatomic nonmetal"),
- ("Kr", 36, 18, 4, "krypton", 83.8000, "noble gas"),
- ("Rb", 37, 1, 5, "rubidium", 85.4800, "alkali metal"),
- ("Sr", 38, 2, 5, "strontium", 87.6200, "alkaline earth metal"),
- ("Y", 39, 3, 5, "yttrium", 88.9050, "transition metal"),
- ("Zr", 40, 4, 5, "zirconium", 91.2200, "transition metal"),
- ("Nb", 41, 5, 5, "niobium", 92.9060, "transition metal"),
- ("Mo", 42, 6, 5, "molybdenum", 95.9500, "transition metal"),
- ("Tc", 43, 7, 5, "technetium", 99.0000, "transition metal"),
- ("Ru", 44, 8, 5, "ruthenium", 101.0700, "transition metal"),
- ("Rh", 45, 9, 5, "rhodium", 102.9100, "transition metal"),
- ("Pd", 46, 10, 5, "palladium", 106.400, "transition metal"),
- ("Ag", 47, 11, 5, "silver", 107.880, "transition metal"),
- ("Cd", 48, 12, 5, "cadmium", 112.410, "transition metal"),
- ("In", 49, 13, 5, "indium", 114.820, "post transition metal"),
- ("Sn", 50, 14, 5, "tin", 118.690, "post transition metal"),
- ("Sb", 51, 15, 5, "antimony", 121.760, "metalloid"),
- ("Te", 52, 16, 5, "tellurium", 127.600, "metalloid"),
- ("I", 53, 17, 5, "iodine", 126.910, "diatomic nonmetal"),
- ("Xe", 54, 18, 5, "xenon", 131.300, "noble gas"),
- ("Cs", 55, 1, 6, "caesium", 132.910, "alkali metal"),
- ("Ba", 56, 2, 6, "barium", 137.360, "alkaline earth metal"),
- ("La", 57, 3, 6, "lanthanum", 138.920, "lanthanide"),
- ("Ce", 58, 4, 9, "cerium", 140.130, "lanthanide"),
- ("Pr", 59, 5, 9, "praseodymium", 140.920, "lanthanide"),
- ("Nd", 60, 6, 9, "neodymium", 144.270, "lanthanide"),
- ("Pm", 61, 7, 9, "promethium", 147.000, "lanthanide"),
- ("Sm", 62, 8, 9, "samarium", 150.350, "lanthanide"),
- ("Eu", 63, 9, 9, "europium", 152.000, "lanthanide"),
- ("Gd", 64, 10, 9, "gadolinium", 157.260, "lanthanide"),
- ("Tb", 65, 11, 9, "terbium", 158.930, "lanthanide"),
- ("Dy", 66, 12, 9, "dysprosium", 162.510, "lanthanide"),
- ("Ho", 67, 13, 9, "holmium", 164.940, "lanthanide"),
- ("Er", 68, 14, 9, "erbium", 167.270, "lanthanide"),
- ("Tm", 69, 15, 9, "thulium", 168.940, "lanthanide"),
- ("Yb", 70, 16, 9, "ytterbium", 173.040, "lanthanide"),
- ("Lu", 71, 17, 9, "lutetium", 174.990, "lanthanide"),
- ("Hf", 72, 4, 6, "hafnium", 178.500, "transition metal"),
- ("Ta", 73, 5, 6, "tantalum", 180.950, "transition metal"),
- ("W", 74, 6, 6, "tungsten", 183.920, "transition metal"),
- ("Re", 75, 7, 6, "rhenium", 186.200, "transition metal"),
- ("Os", 76, 8, 6, "osmium", 190.200, "transition metal"),
- ("Ir", 77, 9, 6, "iridium", 192.200, "transition metal"),
- ("Pt", 78, 10, 6, "platinum", 195.090, "transition metal"),
- ("Au", 79, 11, 6, "gold", 197.200, "transition metal"),
- ("Hg", 80, 12, 6, "mercury", 200.610, "transition metal"),
- ("Tl", 81, 13, 6, "thallium", 204.390, "post transition metal"),
- ("Pb", 82, 14, 6, "lead", 207.210, "post transition metal"),
- ("Bi", 83, 15, 6, "bismuth", 209.000, "post transition metal"),
- ("Po", 84, 16, 6, "polonium", 209.000, "post transition metal"),
- ("At", 85, 17, 6, "astatine", 210.000, "metalloid"),
- ("Rn", 86, 18, 6, "radon", 222.000, "noble gas"),
- ("Fr", 87, 1, 7, "francium", 223.000, "alkali metal"),
- ("Ra", 88, 2, 7, "radium", 226.000, "alkaline earth metal"),
- ("Ac", 89, 3, 7, "actinium", 227.000, "actinide"),
- ("Th", 90, 4, 10, "thorium", 232.000, "actinide"),
- ("Pa", 91, 5, 10, "proactinium", 231.03588, "actinide"),
- ("U", 92, 6, 10, "uranium", 238.070, "actinide"),
- ("Np", 93, 7, 10, "neptunium", 237.000, "actinide"),
- ("Pu", 94, 8, 10, "plutonium", 239.100, "actinide"),
- ("Am", 95, 9, 10, "americium", 243, "actinide"),
- ("Cm", 96, 10, 10, "curium", 247, "actinide"),
- ("Bk", 97, 11, 10, "berkelium", 247, "actinide"),
- ("Cf", 98, 12, 10, "californium", 251, "actinide"),
- ("Es", 99, 13, 10, "einsteinium", 252, "actinide"),
- ("Fm", 100, 14, 10, "fermium", 257, "actinide"),
- ("Md", 101, 15, 10, "mendelevium", 258, "actinide"),
- ("No", 102, 16, 10, "nobelium", 259, "actinide"),
- ("Lr", 103, 17, 10, "lawrencium", 262, "actinide"),
- ("Rf", 104, 4, 7, "rutherfordium", 261, "transition metal"),
- ("Db", 105, 5, 7, "dubnium", 262, "transition metal"),
- ("Sg", 106, 6, 7, "seaborgium", 266, "transition metal"),
- ("Bh", 107, 7, 7, "bohrium", 264, "transition metal"),
- ("Hs", 108, 8, 7, "hassium", 269, "transition metal"),
- ("Mt", 109, 9, 7, "meitnerium", 268)]
-
-
-class PeriodicTableItem(object):
- """Periodic table item, used as generic item in :class:`PeriodicTable`,
- :class:`PeriodicCombo` and :class:`PeriodicList`.
-
- This implementation stores the minimal amount of information needed by the
- widgets:
-
- - atomic symbol
- - atomic number
- - element name
- - atomic mass
- - column of element in periodic table
- - row of element in periodic table
-
- You can subclass this class to add additional information.
-
- :param str symbol: Atomic symbol (e.g. H, He, Li...)
- :param int Z: Proton number
- :param int col: 1-based column index of element in periodic table
- :param int row: 1-based row index of element in periodic table
- :param str name: PeriodicTableItem name ("hydrogen", ...)
- :param float mass: Atomic mass (gram per mol)
- :param str subcategory: Subcategory, based on physical properties
- (e.g. "alkali metal", "noble gas"...)
- """
- def __init__(self, symbol, Z, col, row, name, mass,
- subcategory=""):
- self.symbol = symbol
- """Atomic symbol (e.g. H, He, Li...)"""
- self.Z = Z
- """Atomic number (Proton number)"""
- self.col = col
- """1-based column index of element in periodic table"""
- self.row = row
- """1-based row index of element in periodic table"""
- self.name = name
- """PeriodicTableItem name ("hydrogen", ...)"""
- self.mass = mass
- """Atomic mass (gram per mol)"""
- self.subcategory = subcategory
- """Subcategory, based on physical properties
- (e.g. "alkali metal", "noble gas"...)"""
-
- # pymca compatibility (elements used to be stored as a list of lists)
- def __getitem__(self, idx):
- if idx == 6:
- _logger.warning("density not implemented in silx, returning 0.")
-
- ret = [self.symbol, self.Z,
- self.col, self.row,
- self.name, self.mass,
- 0.]
- return ret[idx]
-
- def __len__(self):
- return 6
-
-
-class ColoredPeriodicTableItem(PeriodicTableItem):
- """:class:`PeriodicTableItem` with an added :attr:`bgcolor`.
- The background color can be passed as a parameter to the constructor.
- If it is not specified, it will be defined based on
- :attr:`subcategory`.
-
- :param str bgcolor: Custom background color for element in
- periodic table, as a RGB string *#RRGGBB*"""
- COLORS = {
- "diatomic nonmetal": "#7FFF00", # chartreuse
- "noble gas": "#00FFFF", # cyan
- "alkali metal": "#FFE4B5", # Moccasin
- "alkaline earth metal": "#FFA500", # orange
- "polyatomic nonmetal": "#7FFFD4", # aquamarine
- "transition metal": "#FFA07A", # light salmon
- "metalloid": "#8FBC8F", # Dark Sea Green
- "post transition metal": "#D3D3D3", # light gray
- "lanthanide": "#FFB6C1", # light pink
- "actinide": "#F08080", # Light Coral
- "": "#FFFFFF" # white
- }
- """Dictionary defining RGB colors for each subcategory."""
-
- def __init__(self, symbol, Z, col, row, name, mass,
- subcategory="", bgcolor=None):
- PeriodicTableItem.__init__(self, symbol, Z, col, row, name, mass,
- subcategory)
-
- self.bgcolor = self.COLORS.get(subcategory, "#FFFFFF")
- """Background color of element in the periodic table,
- based on its subcategory. This should be a string of a hexadecimal
- RGB code, with the format *#RRGGBB*.
- If the subcategory is unknown, use white (*#FFFFFF*)
- """
-
- # possible custom color
- if bgcolor is not None:
- self.bgcolor = bgcolor
-
-
-_defaultTableItems = [ColoredPeriodicTableItem(*info) for info in _elements]
-
-
-class _ElementButton(qt.QPushButton):
- """Atomic element button, used as a cell in the periodic table
- """
- sigElementEnter = qt.pyqtSignal(object)
- """Signal emitted as the cursor enters the widget"""
- sigElementLeave = qt.pyqtSignal(object)
- """Signal emitted as the cursor leaves the widget"""
- sigElementClicked = qt.pyqtSignal(object)
- """Signal emitted when the widget is clicked"""
-
- def __init__(self, item, parent=None):
- """
-
- :param parent: Parent widget
- :param PeriodicTableItem item: :class:`PeriodicTableItem` object
- """
- qt.QPushButton.__init__(self, parent)
-
- self.item = item
- """:class:`PeriodicTableItem` object represented by this button"""
-
- self.setText(item.symbol)
- self.setFlat(1)
- self.setCheckable(0)
-
- self.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Expanding,
- qt.QSizePolicy.Expanding))
-
- self.selected = False
- self.current = False
-
- # selection colors
- self.selected_color = qt.QColor(qt.Qt.yellow)
- self.current_color = qt.QColor(qt.Qt.gray)
- self.selected_current_color = qt.QColor(qt.Qt.darkYellow)
-
- # element colors
-
- if hasattr(item, "bgcolor"):
- self.bgcolor = qt.QColor(item.bgcolor)
- else:
- self.bgcolor = qt.QColor("#FFFFFF")
-
- self.brush = qt.QBrush()
- self.__setBrush()
-
- self.clicked.connect(self.clickedSlot)
-
- def sizeHint(self):
- return qt.QSize(40, 40)
-
- def setCurrent(self, b):
- """Set this element button as current.
- Multiple buttons can be selected.
-
- :param b: boolean
- """
- self.current = b
- self.__setBrush()
-
- def isCurrent(self):
- """
- :return: True if element button is current
- """
- return self.current
-
- def isSelected(self):
- """
- :return: True if element button is selected
- """
- return self.selected
-
- def setSelected(self, b):
- """Set this element button as selected.
- Only a single button can be selected.
-
- :param b: boolean
- """
- self.selected = b
- self.__setBrush()
-
- def __setBrush(self):
- """Selected cells are yellow when not current.
- The current cell is dark yellow when selected or grey when not
- selected.
- Other cells have no bg color by default, unless specified at
- instantiation (:attr:`bgcolor`)"""
- palette = self.palette()
- # if self.current and self.selected:
- # self.brush = qt.QBrush(self.selected_current_color)
- # el
- if self.selected:
- self.brush = qt.QBrush(self.selected_color)
- # elif self.current:
- # self.brush = qt.QBrush(self.current_color)
- elif self.bgcolor is not None:
- self.brush = qt.QBrush(self.bgcolor)
- else:
- self.brush = qt.QBrush()
- palette.setBrush(self.backgroundRole(),
- self.brush)
- self.setPalette(palette)
- self.update()
-
- def paintEvent(self, pEvent):
- # get button geometry
- widgGeom = self.rect()
- paintGeom = qt.QRect(widgGeom.left() + 1,
- widgGeom.top() + 1,
- widgGeom.width() - 2,
- widgGeom.height() - 2)
-
- # paint background color
- painter = qt.QPainter(self)
- if self.brush is not None:
- painter.fillRect(paintGeom, self.brush)
- # paint frame
- pen = qt.QPen(qt.Qt.black)
- pen.setWidth(1 if not self.isCurrent() else 5)
- painter.setPen(pen)
- painter.drawRect(paintGeom)
- painter.end()
- qt.QPushButton.paintEvent(self, pEvent)
-
- def enterEvent(self, e):
- """Emit a :attr:`sigElementEnter` signal and send a
- :class:`PeriodicTableItem` object"""
- self.sigElementEnter.emit(self.item)
-
- def leaveEvent(self, e):
- """Emit a :attr:`sigElementLeave` signal and send a
- :class:`PeriodicTableItem` object"""
- self.sigElementLeave.emit(self.item)
-
- def clickedSlot(self):
- """Emit a :attr:`sigElementClicked` signal and send a
- :class:`PeriodicTableItem` object"""
- self.sigElementClicked.emit(self.item)
-
-
-class PeriodicTable(qt.QWidget):
- """Periodic Table widget
-
- .. image:: img/PeriodicTable.png
-
- The following example shows how to connect clicking to selection::
-
- from silx.gui import qt
- from silx.gui.widgets.PeriodicTable import PeriodicTable
- app = qt.QApplication([])
- pt = PeriodicTable()
- pt.sigElementClicked.connect(pt.elementToggle)
- pt.show()
- app.exec_()
-
- To print all selected elements each time a new element is selected::
-
- def my_slot(item):
- pt.elementToggle(item)
- selected_elements = pt.getSelection()
- for e in selected_elements:
- print(e.symbol)
-
- pt.sigElementClicked.connect(my_slot)
-
- """
- sigElementClicked = qt.pyqtSignal(object)
- """When any element is clicked in the table, the widget emits
- this signal and sends a :class:`PeriodicTableItem` object.
- """
-
- sigSelectionChanged = qt.pyqtSignal(object)
- """When any element is selected/unselected in the table, the widget emits
- this signal and sends a list of :class:`PeriodicTableItem` objects.
-
- .. note::
-
- To enable selection of elements, you must set *selectable=True*
- when you instantiate the widget. Alternatively, you can also connect
- :attr:`sigElementClicked` to :meth:`elementToggle` manually::
-
- pt = PeriodicTable()
- pt.sigElementClicked.connect(pt.elementToggle)
-
-
- :param parent: parent QWidget
- :param str name: Widget window title
- :param elements: List of items (:class:`PeriodicTableItem` objects) to
- be represented in the table. By default, take elements from
- a predefined list with minimal information (symbol, atomic number,
- name, mass).
- :param bool selectable: If *True*, multiple elements can be
- selected by clicking with the mouse. If *False* (default),
- selection is only possible with method :meth:`setSelection`.
- """
-
- def __init__(self, parent=None, name="PeriodicTable", elements=None,
- selectable=False):
- self.selectable = selectable
- qt.QWidget.__init__(self, parent)
- self.setWindowTitle(name)
- self.gridLayout = qt.QGridLayout(self)
- self.gridLayout.setContentsMargins(0, 0, 0, 0)
- self.gridLayout.addItem(qt.QSpacerItem(0, 5), 7, 0)
-
- for idx in range(10):
- self.gridLayout.setRowStretch(idx, 3)
- # row 8 (above lanthanoids is empty)
- self.gridLayout.setRowStretch(7, 2)
-
- # Element information displayed when cursor enters a cell
- self.eltLabel = qt.QLabel(self)
- f = self.eltLabel.font()
- f.setBold(1)
- self.eltLabel.setFont(f)
- self.eltLabel.setAlignment(qt.Qt.AlignHCenter)
- self.gridLayout.addWidget(self.eltLabel, 1, 1, 3, 10)
-
- self._eltCurrent = None
- """Current :class:`_ElementButton` (last clicked)"""
-
- self._eltButtons = OrderedDict()
- """Dictionary of all :class:`_ElementButton`. Keys are the symbols
- ("H", "He", "Li"...)"""
-
- if elements is None:
- elements = _defaultTableItems
- # fill cells with elements
- for elmt in elements:
- self.__addElement(elmt)
-
- def __addElement(self, elmt):
- """Add one :class:`_ElementButton` widget into the grid,
- connect its signals to interact with the cursor"""
- b = _ElementButton(elmt, self)
- b.setAutoDefault(False)
-
- self._eltButtons[elmt.symbol] = b
- self.gridLayout.addWidget(b, elmt.row, elmt.col)
-
- b.sigElementEnter.connect(self.elementEnter)
- b.sigElementLeave.connect(self._elementLeave)
- b.sigElementClicked.connect(self._elementClicked)
-
- def elementEnter(self, item):
- """Update label with element info (e.g. "Nb(41) - niobium")
- when mouse cursor hovers an element.
-
- :param PeriodicTableItem item: Element entered by cursor
- """
- self.eltLabel.setText("%s(%d) - %s" % (item.symbol, item.Z, item.name))
-
- def _elementLeave(self, item):
- """Clear label when the cursor leaves the cell
-
- :param PeriodicTableItem item: Element left
- """
- self.eltLabel.setText("")
-
- def _elementClicked(self, item):
- """Emit :attr:`sigElementClicked`,
- toggle selected state of element
-
- :param PeriodicTableItem item: Element clicked
- """
- if self._eltCurrent is not None:
- self._eltCurrent.setCurrent(False)
- self._eltButtons[item.symbol].setCurrent(True)
- self._eltCurrent = self._eltButtons[item.symbol]
- if self.selectable:
- self.elementToggle(item)
- self.sigElementClicked.emit(item)
-
- def getSelection(self):
- """Return a list of selected elements, as a list of :class:`PeriodicTableItem`
- objects.
-
- :return: Selected items
- :rtype: List[PeriodicTableItem]
- """
- return [b.item for b in self._eltButtons.values() if b.isSelected()]
-
- def setSelection(self, symbols):
- """Set selected elements.
-
- This causes the sigSelectionChanged signal
- to be emitted, even if the selection didn't actually change.
-
- :param List[str] symbols: List of symbols of elements to be selected
- (e.g. *["Fe", "Hg", "Li"]*)
- """
- # accept list of PeriodicTableItems as input, because getSelection
- # returns these objects and it makes sense to have getter and setter
- # use same type of data
- if isinstance(symbols[0], PeriodicTableItem):
- symbols = [elmt.symbol for elmt in symbols]
-
- for (e, b) in self._eltButtons.items():
- b.setSelected(e in symbols)
- self.sigSelectionChanged.emit(self.getSelection())
-
- def setElementSelected(self, symbol, state):
- """Modify *selected* status of a single element (select or unselect)
-
- :param str symbol: PeriodicTableItem symbol to be selected
- :param bool state: *True* to select, *False* to unselect
- """
- self._eltButtons[symbol].setSelected(state)
- self.sigSelectionChanged.emit(self.getSelection())
-
- def isElementSelected(self, symbol):
- """Return *True* if element is selected, else *False*
-
- :param str symbol: PeriodicTableItem symbol
- :return: *True* if element is selected, else *False*
- """
- return self._eltButtons[symbol].isSelected()
-
- def elementToggle(self, item):
- """Toggle selected/unselected state for element
-
- :param item: PeriodicTableItem object
- """
- b = self._eltButtons[item.symbol]
- b.setSelected(not b.isSelected())
- self.sigSelectionChanged.emit(self.getSelection())
-
-
-class PeriodicCombo(qt.QComboBox):
- """
- Combo list with all atomic elements of the periodic table
-
- .. image:: img/PeriodicCombo.png
-
- :param bool detailed: True (default) display element symbol, Z and name.
- False display only element symbol and Z.
- :param elements: List of items (:class:`PeriodicTableItem` objects) to
- be represented in the table. By default, take elements from
- a predefined list with minimal information (symbol, atomic number,
- name, mass).
- """
- sigSelectionChanged = qt.pyqtSignal(object)
- """Signal emitted when the selection changes. Send
- :class:`PeriodicTableItem` object representing selected
- element
- """
-
- def __init__(self, parent=None, detailed=True, elements=None):
- qt.QComboBox.__init__(self, parent)
-
- # add all elements from global list
- if elements is None:
- elements = _defaultTableItems
- for i, elmt in enumerate(elements):
- if detailed:
- txt = "%2s (%d) - %s" % (elmt.symbol, elmt.Z, elmt.name)
- else:
- txt = "%2s (%d)" % (elmt.symbol, elmt.Z)
- self.insertItem(i, txt)
-
- self.currentIndexChanged[int].connect(self.__selectionChanged)
-
- def __selectionChanged(self, idx):
- """Emit :attr:`sigSelectionChanged`"""
- self.sigSelectionChanged.emit(_defaultTableItems[idx])
-
- def getSelection(self):
- """Get selected element
-
- :return: Selected element
- :rtype: PeriodicTableItem
- """
- return _defaultTableItems[self.currentIndex()]
-
- def setSelection(self, symbol):
- """Set selected item in combobox by giving the atomic symbol
-
- :param symbol: Symbol of element to be selected
- """
- # accept PeriodicTableItem for getter/setter consistency
- if isinstance(symbol, PeriodicTableItem):
- symbol = symbol.symbol
- symblist = [elmt.symbol for elmt in _defaultTableItems]
- self.setCurrentIndex(symblist.index(symbol))
-
-
-class PeriodicList(qt.QTreeWidget):
- """List of atomic elements in a :class:`QTreeView`
-
- .. image:: img/PeriodicList.png
-
- :param QWidget parent: Parent widget
- :param bool detailed: True (default) display element symbol, Z and name.
- False display only element symbol and Z.
- :param single: *True* for single element selection with mouse click,
- *False* for multiple element selection mode.
- """
- sigSelectionChanged = qt.pyqtSignal(object)
- """When any element is selected/unselected in the widget, it emits
- this signal and sends a list of currently selected
- :class:`PeriodicTableItem` objects.
- """
-
- def __init__(self, parent=None, detailed=True, single=False, elements=None):
- qt.QTreeWidget.__init__(self, parent)
-
- self.detailed = detailed
-
- headers = ["Z", "Symbol"]
- if detailed:
- headers.append("Name")
- self.setColumnCount(3)
- else:
- self.setColumnCount(2)
- self.setHeaderLabels(headers)
- self.header().setStretchLastSection(False)
-
- self.setRootIsDecorated(0)
- self.itemClicked.connect(self.__selectionChanged)
- self.setSelectionMode(qt.QAbstractItemView.SingleSelection if single
- else qt.QAbstractItemView.ExtendedSelection)
- self.__fill_widget(elements)
- self.resizeColumnToContents(0)
- self.resizeColumnToContents(1)
- if detailed:
- self.resizeColumnToContents(2)
-
- def __fill_widget(self, elements):
- """Fill tree widget with elements """
- if elements is None:
- elements = _defaultTableItems
-
- self.tree_items = []
-
- previous_item = None
- for elmt in elements:
- if previous_item is None:
- item = qt.QTreeWidgetItem(self)
- else:
- item = qt.QTreeWidgetItem(self, previous_item)
- item.setText(0, str(elmt.Z))
- item.setText(1, elmt.symbol)
- if self.detailed:
- item.setText(2, elmt.name)
- self.tree_items.append(item)
- previous_item = item
-
- def __selectionChanged(self, treeItem, column):
- """Emit a :attr:`sigSelectionChanged` and send a list of
- :class:`PeriodicTableItem` objects."""
- self.sigSelectionChanged.emit(self.getSelection())
-
- def getSelection(self):
- """Get a list of selected elements, as a list of :class:`PeriodicTableItem`
- objects.
-
- :return: Selected elements
- :rtype: List[PeriodicTableItem]"""
- return [_defaultTableItems[idx] for idx in range(len(self.tree_items))
- if self.tree_items[idx].isSelected()]
-
- # setSelection is a bad name (name of a QTreeWidget method)
- def setSelectedElements(self, symbolList):
- """
-
- :param symbolList: List of atomic symbols ["H", "He", "Li"...]
- to be selected in the widget
- """
- # accept PeriodicTableItem for getter/setter consistency
- if isinstance(symbolList[0], PeriodicTableItem):
- symbolList = [elmt.symbol for elmt in symbolList]
- for idx in range(len(self.tree_items)):
- self.tree_items[idx].setSelected(_defaultTableItems[idx].symbol in symbolList)