#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''Pychemqt, Chemical Engineering Process simulator
Copyright (C) 2009-2017, Juan José Gómez Romera <jjgomera@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.'''


###############################################################################
# Module to define common graphics widget
#   - Status: Label with status (for equipment, stream)
#   - Entrada_con_unidades: Composite widget for unit values for input/view
#   - Tabla: Custom tablewidget tablewidget with added functionality
#   - ClickableLabel: Label with custom clicked signal
#   - ColorSelector: Composite widget for colour definition
#   - DragButton: Button with drag & drop support
#   - PathConfig: Custom widget for file path show and configure
#   - LineConfig: Custom QGroupbox with all matplotlib Line configuration
#   - CustomCombo: General custom QComboBox
#   - LineStyleCombo: Custom QComboBox for select matplotlib line styles
#   - MarkerCombo: Custom QComboBox for select matplotlib line marker
#   - NumericFactor: Numeric format configuration dialog
#   - InputFont: Custom widget to edit a text input with font and color support
#   - Table_Graphics: Custom widget to implement a popup in PFD
#   - QLabelMath: Customized QLabel to show a pixmap from a latex code

#   - createAction
#   - okToContinue: Function to ask user if any unsaved change
#   - mathTex2QPixmap: Convert a latex text to a QPixmap
###############################################################################


from configparser import ConfigParser
from math import pi
import os
import sys

from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg

from lib.config import conf_dir, IMAGE_PATH
from lib.corriente import Corriente
from lib.utilities import representacion
from tools.UI_unitConverter import UI_conversorUnidades, moneda
from UI.delegate import CellEditor


class Status(QtWidgets.QLabel):
    """Widget with status of dialog, equipment, stream, project, ..."""
    status = (
        (0, QtWidgets.QApplication.translate("pychemqt", "Underspecified"),
         "yellow"),
        (1, QtWidgets.QApplication.translate("pychemqt", "Solved"), "green"),
        (2, QtWidgets.QApplication.translate("pychemqt", "Ignored"),
         "Light gray"),
        (3, QtWidgets.QApplication.translate("pychemqt", "Warning"), "green"),
        (4, QtWidgets.QApplication.translate("pychemqt", "Calculating..."),
         "Cyan"),
        (5, QtWidgets.QApplication.translate("pychemqt", "Error"),  "red"))

    def __init__(self, state=0, text="", parent=None):
        """
        state:
            0   -   Not solved
            1   -   OK
            2   -   Ignore
            3   -   Warning (Recommend: Use text parameter to explain)
            4   -   Calculating
            5   -   Error
        """
        super(Status, self).__init__(parent)
        self.setState(state)
        self.setAlignment(QtCore.Qt.AlignCenter)
        self.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.setFrameShape(QtWidgets.QFrame.Panel)
        self.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                           QtWidgets.QSizePolicy.Preferred)
        self.oldState = 0
        self.oldText = ""

    def setState(self, state, text=""):
        """Change the state"""

        if state == 2:
            self.oldState = self.state
            oldtext = self.text().split(": ")
            if len(oldtext) == 1:
                self.oldText = ""
            else:
                self.oldText = oldtext[1:].join(": ")
        if text:
            self.setText(self.status[state][1]+": "+text)
        else:
            self.setText(self.status[state][1])
        self.setStyleSheet(
            "QLabel { background-color: %s}" % self.status[state][2])
        QtWidgets.QApplication.processEvents()
        self.state = state

    def restaurar(self):
        """Restore old stade"""
        self.setState(self.oldState, self.oldText)


class Entrada_con_unidades(QtWidgets.QWidget):
    """Customized widget with unit functionality"""

    valueChanged = QtCore.pyqtSignal(float)

    def __init__(self, unidad, UIconfig=None, retornar=True, readOnly=False,
                 boton=True, texto=True, textounidad="", title="", value=None,
                 start=0, max=float("inf"), min=0, decimales=4, tolerancia=4,
                 parent=None, width=85, resaltado=False, spinbox=False,
                 suffix="", step=0.01, colorReadOnly=None, colorResaltado=None,
                 frame=True, showNull=False):
        """
        Units:
            unidad: The unit (lib/unidades class) to use, mandatory
            UIconfig: Magnitud necessary if the main unit have several meaning
            title: Update unit title property
            retornar: Boolean to let or avoid the conversion window update
                the value of widget
            value: Inicial value of widget
            max: Maximum value for widget
            min: Minimum value for widget
            decimales: Decimal number count to show of value
            tolerancia: Value of exponent over than to use exponential notation
        UI:
            readOnly: Boolean, set widget readOnly property
            frame: Boolean, show the frame of widget or not
            width: Width of value widget
            boton: Boolean, show or not the button for unit conversion dialog
            texto: Boolean, show the unit text at right of value
            textounidad: Alternate text to show as unit text
            suffix: Text added to value in value representation
            showNull: Boolean, show value if it's 0
            resaltado: Boolean, use base color in widget
            colorResaltado: Color to use as base color if value
            colorReadOnly: Color to use is the widget is readOnly
        Spinbox functionality:
            spinbox: boolean to specified a QSpinbox use, with mouse response
            start: initial value for spinbox mouse interaction
            step: value of step at mouse spingox interaction
        """
        super(Entrada_con_unidades, self).__init__(parent)
        self.resize(self.minimumSize())
        self.unidad = unidad

        if title:
            self.unidad.__title__ = title
        if unidad == float or unidad == int:
            self.magnitud = None
        else:
            self.magnitud = unidad.__name__
        if unidad == int and spinbox and step == 0.01:
            step = 1
        self.decimales = decimales
        self.tolerancia = tolerancia
        self.step = step
        self.spinbox = spinbox
        self.max = max
        self.suffix = suffix
        self.min = min
        self.start = start
        self.textounidad = textounidad
        self.boton = boton
        self.resaltado = resaltado
        self.showNull = showNull

        Config = ConfigParser()
        Config.read(conf_dir+"pychemqtrc")
        if colorReadOnly:
            self.colorReadOnly = colorReadOnly
        else:
            self.colorReadOnly = QtGui.QColor(
                Config.get("General", 'Color_ReadOnly'))
        if colorResaltado:
            self.colorResaltado = colorResaltado
        else:
            self.colorResaltado = QtGui.QColor(
                Config.get("General", 'Color_Resaltado'))

        if UIconfig:
            self.UIconfig = UIconfig
        else:
            self.UIconfig = self.magnitud
        self.retornar = retornar
        layout = QtWidgets.QGridLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.entrada = QtWidgets.QLineEdit()
        self.entrada.setFixedSize(width, 24)
        self.entrada.editingFinished.connect(self.entrada_editingFinished)
        self.entrada.setAlignment(
            QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        if unidad == int:
            if max == float("inf"):
                max = 1000000000
            validator = QtGui.QIntValidator(min, max, self)
        else:
            validator = QtGui.QDoubleValidator(min, max, decimales, self)
            locale = QtCore.QLocale("en")
            validator.setLocale(locale)
        self.entrada.setValidator(validator)
        self.setReadOnly(readOnly)
        self.setRetornar(self.retornar)
        self.setFrame(frame)
        layout.addWidget(self.entrada, 0, 1, 1, 3)

        if value is None:
            self.value = self.unidad(0)
        else:
            self.setValue(value)
        if self.magnitud:
            if boton:
                self.unidades = QtWidgets.QPushButton(".")
                self.unidades.setFixedSize(12, 24)
                self.unidades.setVisible(False)
                self.unidades.clicked.connect(self.unidades_clicked)
                layout.addWidget(self.unidades, 0, 1)

        if boton:
            self.botonClear = QtWidgets.QPushButton(QtGui.QIcon(QtGui.QPixmap(
                os.environ["pychemqt"] +
                os.path.join("images", "button", "editDelete.png"))), "")
            self.botonClear.setFixedSize(12, 24)
            self.botonClear.setVisible(False)
            self.botonClear.clicked.connect(self.clear)
            layout.addWidget(self.botonClear, 0, 3)

        if texto:
            self.texto = QtWidgets.QLabel()
            self.texto.setAlignment(QtCore.Qt.AlignVCenter)
            self.texto.setIndent(5)
            txt = ""
            if self.UIconfig:
                txt += self.value.text(self.UIconfig)
            if textounidad:
                txt += textounidad
            self.texto.setText(txt)
            layout.addWidget(self.texto, 0, 4)

        layout.addItem(QtWidgets.QSpacerItem(
            0, 0, QtWidgets.QSizePolicy.Expanding,
            QtWidgets.QSizePolicy.Fixed), 0, 5)
        self.setResaltado(resaltado)

    def unidades_clicked(self):
        """Show the unit converter dialog"""
        if self.magnitud == "Currency":
            dialog = moneda(self.value)
        else:
            dialog = UI_conversorUnidades(self.unidad, self.value)

        if dialog.exec_() and self.retornar:
            # Change the value if change and retornar if active
            self.entrada.setText(
                representacion(dialog.value.config(self.UIconfig))+self.suffix)
            oldvalue = self.value
            self.value = dialog.value
            if oldvalue != self.value:
                self.valueChanged.emit(self.value)

    def entrada_editingFinished(self):
        """Change the value at finish of edit"""
        if not self.readOnly:
            # Filter suffix and fix bad numeric , interpretation
            if self.suffix:
                txt = self.entrada.text().split(self.suffix).replace(',', '.')
            else:
                txt = self.entrada.text().replace(',', '.')
            if self.unidad != int:
                self.entrada.setText(
                    representacion(float(txt), decimales=self.decimales,
                                   tol=self.tolerancia)+self.suffix)
            oldvalue = self.value
            if self.magnitud:
                self.value = self.unidad(
                    float(txt), "conf", magnitud=self.UIconfig)
            else:
                self.value = self.unidad(txt)
            if self.value != oldvalue:
                self.valueChanged.emit(self.value)
                self.setToolTip()

    def clear(self):
        """Clear value"""
        self.entrada.setText("")
        self.value = None

    def setResaltado(self, bool):
        self.resaltado = bool
        paleta = QtGui.QPalette()
        if bool:
            paleta.setColor(
                QtGui.QPalette.Base, QtGui.QColor(self.colorResaltado))
        elif self.readOnly:
            paleta.setColor(
                QtGui.QPalette.Base, QtGui.QColor(self.colorReadOnly))
        else:
            paleta.setColor(QtGui.QPalette.Base, QtGui.QColor("white"))
        self.entrada.setPalette(paleta)

    def setReadOnly(self, readOnly):
        self.entrada.setReadOnly(readOnly)
        self.readOnly = readOnly
        self.setResaltado(self.resaltado)

    def setNotReadOnly(self, editable):
        self.setReadOnly(not editable)

    def setRetornar(self, retornar):
        self.retornar = retornar

    def setValue(self, value):
        if value is None:
            value = 0
        self.value = self.unidad(value)
        if value or self.showNull:
            if self.magnitud:
                self.entrada.setText(
                    self.value.format(magnitud=self.UIconfig)+self.suffix)
            elif self.unidad == float:
                self.entrada.setText(
                    representacion(self.value, decimales=self.decimales,
                                   tol=self.tolerancia)+self.suffix)
            else:
                self.entrada.setText(str(self.value)+self.suffix)
            self.setToolTip()

    def setFrame(self, frame):
        self.entrada.setFrame(frame)
        self.frame = frame

    def setToolTip(self):
        """Define the tooltip with the values in confguration"""
        Preferences = ConfigParser()
        Preferences.read(conf_dir+"pychemqtrc")
        if Preferences.getboolean("Tooltip", "Show"):
            Config = ConfigParser()
            Config.read(conf_dir+"pychemqtrc")
            try:
                lista = eval(Config.get('Tooltip', self.magnitud))
            except:
                lista = []
            if len(lista) > 0:
                valores = []
                for i in lista:
                    valores.append(representacion(
                        self.value.__getattribute__(self.value.__units__[i]),
                        self.decimales, self.tolerancia) + " " +
                        self.value.__text__[i])
                self.entrada.setToolTip(os.linesep.join(valores))

    def keyPressEvent(self, e):
        """Manage the key press to emulate a QSpinbox"""
        if not self.readOnly:
            if e.key() in [QtCore.Qt.Key_Insert, QtCore.Qt.Key_Backspace]:
                self.clear()
            if self.spinbox:
                if not self.value:
                    self.value = self.start
                if e.key() == QtCore.Qt.Key_Up:
                    valor = self.value+self.step
                    if valor > self.max:
                        self.setValue(self.max)
                    else:
                        self.setValue(valor)
                elif e.key() == QtCore.Qt.Key_Down:
                    valor = self.value-self.step
                    if valor < self.min:
                        self.setValue(self.min)
                    else:
                        self.setValue(valor)
                self.valueChanged.emit(self.value)

    def enterEvent(self, event):
        """When mouse enter in widget show the unidades and clear button, and
        add margin to let space to clear button"""
        if self.magnitud and self.boton:
            self.unidades.setVisible(True)
        if self.value and self.boton and not self.readOnly:
            self.botonClear.setVisible(True)
            self.entrada.setTextMargins(0, 0, 10, 0)

    def leaveEvent(self, event):
        """When mouse leave the widget undo the enterEvent actions"""
        if self.magnitud and self.boton:
            self.unidades.setVisible(False)
        if self.value and self.boton and not self.readOnly:
            self.botonClear.setVisible(False)
            self.entrada.setTextMargins(0, 0, 0, 0)


class Tabla(QtWidgets.QTableWidget):
    """QTableWidget with custom functionality"""
    editingFinished = QtCore.pyqtSignal()
    rowFinished = QtCore.pyqtSignal(list)

    def __init__(self, columnas=0, filas=0, stretch=True, dinamica=False,
                 readOnly=False, columnReadOnly=None,
                 horizontalHeader=None, verticalHeader=True,
                 verticalHeaderLabels=None, verticalHeaderModel="",
                 verticalOffset=0, orientacion=QtCore.Qt.AlignRight,
                 delegate=CellEditor, delegateforRow=None,
                 parent=None):
        """
        columnas: Column count of widget
        filas: Row count, this value is initial and can be changed
        stretch: Boolean, stretch the last column to fill all space available
        dinamica: Boolean, let user fill the data adding new row when last
            are filled
        readOnly: Boolean, set the readOnly state of widget
        columnReadOnly: Array with boolean for column readOnly state, used when
            the readOnly state is different for each column

        horizontalHeader: Array with text for top header,
            Null don't show the top header
        verticalHeader: Boolean, to show or hide the right header
        verticalHeaderLabels: Array with text for right header
        verticalHeaderModel: If the table is dinamica and the right header is
            show, this are the text model to generate new vertical header label

        verticalOffset: Index, row don't included in fill data, used for show
            info, widget...
        orientation: Horizontal text alignment general for cell in tables,
            default is right as normal for numbers

        delegate: QItemDelegate subclass to configure cell editor
            default CellEditor with float appropiate functionality
        delegateforRow: delegate with specific values for row
        """
        super(Tabla, self).__init__(parent)

        # Dimensions
        self.columnas = columnas
        self.filas = filas+verticalOffset
        self.verticalOffset = verticalOffset
        self.setColumnCount(self.columnas)

        # Header labels
        if not verticalHeader:
            self.verticalHeader().hide()
        if not horizontalHeader:
            self.horizontalHeader().hide()
        else:
            self.setHorizontalHeaderLabels(horizontalHeader)

        self.horizontalHeaderLabel = horizontalHeader
        self.verticalHeaderLabel = verticalHeaderLabels
        self.verticalHeaderBool = verticalHeader
        self.verticalHeaderModel = verticalHeaderModel
        self.horizontalHeader().setStretchLastSection(stretch)

        # readOnly state
        self.readOnly = readOnly
        if readOnly:
            self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
        else:
            self.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
        if columnReadOnly is None:
            self.columnReadOnly = [self.readOnly]*self.columnas
        else:
            self.columnReadOnly = columnReadOnly
            for i in range(self.columnCount()):
                self.setColumnReadOnly(i, columnReadOnly[i])

        # Delegate functionality
        if delegate:
            self.setItemDelegate(delegate(self))
        self.delegateforRow = delegateforRow

        if dinamica:
            self.cellChanged.connect(self.tabla_cellChanged)
        self.dinamica = dinamica

        # self.setAlternatingRowColors(True)
        self.setGridStyle(QtCore.Qt.DotLine)
        self.orientacion = orientacion
        for i in range(filas):
            self.addRow()

    def setConnected(self):
        """The dynamic state can be defined at start or call this procedure"""
        self.cellChanged.connect(self.tabla_cellChanged)
        self.dinamica = True
        if self.rowCount() == 0:
            self.addRow()

    def addRow(self, data=None, index=None):
        """Add row to widget
        data: Array with data to fill new row
        index: Index to add row, default add row at endo of table"""
        if not data:
            data = [""]*self.columnas
        else:
            data = [representacion(i) for i in data]
        if index is not None:
            row = index
        else:
            row = self.rowCount()
        self.insertRow(row)
        self.setRowHeight(row, 22)

        if self.delegateforRow:
            delegate = self.delegateforRow(self.parent())
            self.setItemDelegateForRow(row, delegate)

        Config = ConfigParser()
        Config.read(conf_dir+"pychemqtrc")
        inactivo = QtGui.QColor(Config.get("General", 'Color_ReadOnly'))
        for j in range(self.columnCount()):
            self.setItem(row, j, QtWidgets.QTableWidgetItem(data[j]))
            self.item(row, j).setTextAlignment(
                self.orientacion | QtCore.Qt.AlignVCenter)

            if self.columnReadOnly[j]:
                flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
                self.item(row, j).setBackground(inactivo)
            else:
                flags = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | \
                    QtCore.Qt.ItemIsSelectable
            self.item(row, j).setFlags(flags)

        self.setVHeader(row)

        # Set focus to first editable cell in new row
        if self.dinamica and self.rowCount() > 1:
            columna = self.columnReadOnly.index(False)
            self.setCurrentCell(row, columna)

    def setVHeader(self, row):
        """Set vertical header text"""
        if self.verticalHeaderBool:
            if self.verticalHeaderLabel:
                txt = self.verticalHeaderLabel[row]
            elif self.verticalHeaderModel:
                txt = self.verticalHeaderModel+str(row)
            else:
                txt = str(row+1)
            self.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(txt))

    def tabla_cellChanged(self, i, j):
        """When edit a cell, check status tu add new row"""
        new_line = True
        col = 0
        while col < self.columnas:
            if self.item(i, col).text() != "" or self.columnReadOnly[col]:
                col += 1
            else:
                new_line = False
                break
        if new_line and i == self.rowCount()-1:
            self.addRow()
            fila = self.getRow(i)
            self.rowFinished.emit(fila)

    def getValue(self, row, column):
        """Get value from cell in row and column"""
        txt = self.item(row, column).text()
        try:
            value = float(txt)
        except ValueError:
            value = txt
        return value

    def setValue(self, row, column, value, **fmt):
        """Set value for cell in row and column with text formating"""
        if isinstance(value, float) or isinstance(value, int):
            value = representacion(value, **fmt)
        self.item(row, column).setText(value)
        self.item(row, column).setTextAlignment(
            self.orientacion | QtCore.Qt.AlignVCenter)

    def setColumn(self, column, data, **fmt):
        """Set data for a complete column"""
        while len(data) > self.rowCount()-self.verticalOffset:
            self.addRow()
        for row, dato in enumerate(data):
            self.setValue(row, column, dato, **fmt)

    def getColumn(self, column):
        """Get column data as array"""
        lst = []
        for row in range(self.verticalOffset, self.rowCount()):
            value = self.getValue(row, column)
            if isinstance(value, float):
                lst.append(value)
        return lst

    def getRow(self, row):
        """Get row data as array"""
        lst = []
        for column in range(self.columnCount()):
            lst.append(self.getValue(row, column))
        return lst

    def getData(self):
        """Get all table data as array"""
        matriz = []
        for row in range(self.verticalOffset, self.rowCount()-1):
            lst = self.getRow(row)
            matriz.append(lst)
        return matriz

    def setData(self, matriz):
        """Set table data"""
        for i in range(self.rowCount(), len(matriz)+self.verticalOffset):
            self.addRow()
        for fila in range(self.rowCount()-self.verticalOffset):
            for columna, dato in enumerate(matriz[fila]):
                # self.setVerticalHeaderItem(fila, QtWidgets.QTableWidgetItem(
                    # self.verticalHeaderModel+str(fila)))
                self.item(fila, columna).setText(str(dato))
                self.item(fila+self.verticalOffset, columna).setTextAlignment(
                    self.orientacion | QtCore.Qt.AlignVCenter)
        for i in range(self.verticalOffset, self.rowCount()):
            self.setRowHeight(i+self.verticalOffset, 20)

    def clear(self, size=True):
        """Clear table, remove all data and optionally remove all row"""
        if size:
            self.setRowCount(1+self.verticalOffset)
        for fila in range(self.rowCount()):
            for columna in range(self.columnas):
                    self.item(fila+self.verticalOffset, columna).setText("")

    def setColumnReadOnly(self, column, bool):
        """Set readonly estate per column"""
        if bool:
            flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
        else:
            flags = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | \
                QtCore.Qt.ItemIsSelectable

        for row in range(self.rowCount()):
            self.item(row, column).setFlags(flags)

    def leaveEvent(self, event):
        if self.isEnabled():
            self.editingFinished.emit()


class ClickableLabel(QtWidgets.QLabel):
    """Custom QLabel with clicked functionality"""
    clicked = QtCore.pyqtSignal()

    def mousePressEvent(self, event):
        self.clicked.emit()


class ColorSelector(QtWidgets.QWidget):
    """Color selector widget"""
    valueChanged = QtCore.pyqtSignal('QString')

    def __init__(self, color="#ffffff", alpha=255, isAlpha=False, parent=None):
        super(ColorSelector, self).__init__(parent)

        lyt = QtWidgets.QHBoxLayout(self)
        lyt.setContentsMargins(0, 0, 0, 0)
        lyt.setSpacing(0)

        self.RGB = QtWidgets.QLineEdit()
        self.RGB.editingFinished.connect(self.rgbChanged)
        self.RGB.setFixedSize(80, 24)
        lyt.addWidget(self.RGB)
        self.button = QtWidgets.QToolButton()
        self.button.setFixedSize(24, 24)
        self.button.clicked.connect(self.ColorButtonClicked)
        lyt.addWidget(self.button)
        lyt.addItem(QtWidgets.QSpacerItem(
            20, 20, QtWidgets.QSizePolicy.Expanding,
            QtWidgets.QSizePolicy.Fixed))

        if isAlpha:
            self.isAlpha = QtGui.QColor.HexArgb
        else:
            self.isAlpha = QtGui.QColor.HexRgb

        r = int(color[1:3], 16)
        g = int(color[3:5], 16)
        b = int(color[5:7], 16)
        color = QtGui.QColor(r, g, b, alpha)
        self.setColor(color)

    def setColor(self, color, alpha=255):
        """Set new color value and update text and button color"""
        # Accept color args as a #rgb string too
        if type(color) == str:
            color = QtGui.QColor(color)
            color.setAlpha(alpha)
        self.color = color
        self.button.setStyleSheet("background: %s;" % color.name(self.isAlpha))
        self.RGB.setText(color.name(self.isAlpha))

    def ColorButtonClicked(self):
        """Show the QColorDialog to let user choose new color"""
        dlg = QtWidgets.QColorDialog(self.color, self)
        if self.isAlpha:
            dlg.setOption(QtWidgets.QColorDialog.ShowAlphaChannel)
        if dlg.exec_():
            self.setColor(dlg.currentColor())
            self.valueChanged.emit(dlg.currentColor().name())

    def rgbChanged(self):
        """Let user define the color manually"""
        txt = self.RGB.text()

        # Avoid the editing finished with no changes
        if txt == self.color.name(self.isAlpha):
            return

        # Define the new color from text
        if self.isAlpha:
            alpha = int(txt[1:3], 16)
            r = int(txt[3:5], 16)
            g = int(txt[5:7], 16)
            b = int(txt[7:9], 16)
            color = QtGui.QColor(r, g, b, alpha)
        else:
            color = QtGui.QColor(txt)

        # Only accept new value if it's valid
        if color.isValid():
            self.setColor(color)
            self.valueChanged.emit(color.name(self.isAlpha))


class DragButton(QtWidgets.QToolButton):
    """Clase que define un botón especial que permite arrastrar"""

    def __init__(self, parent=None):
        super(DragButton, self).__init__(parent)

    # def mouseMoveEvent(self, event):
        # self.startDrag()
        # QtWidgets.QToolButton.mouseMoveEvent(self, event)

    # def startDrag(self):
        # if self.icon().isNull():
        #     return
        # data = QtCore.QByteArray()
        # stream = QtCore.QDataStream(data, QtCore.QIODevice.WriteOnly)
        # stream << self.icon()
        # mimeData = QtCore.QMimeData()
        # mimeData.setData("application/x-equipment", data)
        # drag = QtGui.QDrag(self)
        # drag.setMimeData(mimeData)
        # pixmap = self.icon().pixmap(24, 24)
        # drag.setHotSpot(QtCore.QPoint(12, 12))
        # drag.setPixmap(pixmap)
        # drag.exec_(QtCore.Qt.CopyAction)


class PathConfig(QtWidgets.QWidget):
    """Custom widget for a file path show and configure functionality"""
    valueChanged = QtCore.pyqtSignal('QString')

    def __init__(self, title="", path="", patron="", msg="", folder=False,
                 save=False, parent=None):
        """
        title: Optional text an right of widget
        path: Inicial value for file path
        patron: File format to filter in file search dialog
        msg: Title of dialog file
        """
        super(PathConfig, self).__init__(parent)

        self.folder = folder
        self.save = save

        layout = QtWidgets.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        if title:
            layout.addWidget(QtWidgets.QLabel(title))
            layout.addItem(QtWidgets.QSpacerItem(
                10, 10, QtWidgets.QSizePolicy.Fixed,
                QtWidgets.QSizePolicy.Fixed))

        self.path = QtWidgets.QLineEdit()
        self.path.setFixedHeight(24)
        self.path.textEdited.connect(self.pathEdited)
        layout.addWidget(self.path)
        self.boton = QtWidgets.QPushButton(
            QtWidgets.QApplication.translate("pychemqt", "Browse"))
        self.boton.setFixedHeight(24)
        self.boton.clicked.connect(self.select)
        layout.addWidget(self.boton)

        # Define default values for parameters don't defined
        if not patron:
            patron = QtWidgets.QApplication.translate(
                "pychemqt", "All files") + "(*)"
        elif patron == "exe":
            if sys.platform == "win32":
                patron = QtWidgets.QApplication.translate(
                    "pychemqt", "Executable files") + "(*.exe *.bat)"
            else:
                patron = QtWidgets.QApplication.translate(
                    "pychemqt", "All files") + "(*)"
        self.patron = patron

        if not msg:
            msg = QtWidgets.QApplication.translate(
                "pychemqt", "Select path of file")
        self.msg = msg
        self.setText(path)

    def text(self):
        return self.path.text()

    def setText(self, text):
        self.path.setText(text)

    def select(self):
        """Open the QFileDialog to select the file path"""
        dir = os.path.dirname(str(self.path.text()))
        if self.save:
            ruta = QtWidgets.QFileDialog.getSaveFileName(
                self, self.msg, dir, self.patron)[0]
        elif self.folder:
            ruta = QtWidgets.QFileDialog.getExistingDirectory(
                self, self.msg, dir)
        else:
            ruta = QtWidgets.QFileDialog.getOpenFileName(
                self, self.msg, dir, self.patron)[0]
        if ruta:
            self.path.setText(ruta)
            self.valueChanged.emit(ruta)

    def pathEdited(self, path):
        if os.path.isfile(path):
            self.valueChanged.emit(path)


class LineConfig(QtWidgets.QGroupBox):
    """Custom QGroupbox with all matplotlib Line configuration"""

    def __init__(self, confSection, title, parent=None):
        """
        confSection: Name key to identify the line
        title: Title to use in QGroupbox
        """
        super(LineConfig, self).__init__(title, parent)
        self.conf = confSection

        layout = QtWidgets.QVBoxLayout(self)
        lyt1 = QtWidgets.QHBoxLayout()
        self.Width = QtWidgets.QDoubleSpinBox()
        self.Width.setFixedWidth(60)
        self.Width.setAlignment(QtCore.Qt.AlignRight)
        self.Width.setRange(0.1, 5)
        self.Width.setDecimals(1)
        self.Width.setSingleStep(0.1)
        self.Width.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Line width"))
        lyt1.addWidget(self.Width)
        self.Style = LineStyleCombo()
        self.Style.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Line style"))
        lyt1.addWidget(self.Style)
        self.Color = ColorSelector(isAlpha=True)
        self.Color.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Line color"))
        lyt1.addWidget(self.Color)
        self.Marker = MarkerCombo()
        self.Marker.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Line marker"))
        self.Marker.currentIndexChanged.connect(self.changeMarker)
        lyt1.addWidget(self.Marker)
        lyt1.addItem(QtWidgets.QSpacerItem(
            10, 10, QtWidgets.QSizePolicy.Expanding,
            QtWidgets.QSizePolicy.Fixed))
        layout.addLayout(lyt1)

        lyt2 = QtWidgets.QHBoxLayout()
        self.MarkerSize = QtWidgets.QDoubleSpinBox()
        self.MarkerSize.setFixedWidth(60)
        self.MarkerSize.setAlignment(QtCore.Qt.AlignRight)
        self.MarkerSize.setRange(0.1, 5)
        self.MarkerSize.setDecimals(1)
        self.MarkerSize.setSingleStep(0.1)
        self.MarkerSize.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Marker size"))
        lyt2.addWidget(self.MarkerSize)
        self.MarkerColor = ColorSelector()
        self.MarkerColor.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Marker face color"))
        lyt2.addWidget(self.MarkerColor)
        self.EdgeSize = QtWidgets.QDoubleSpinBox()
        self.EdgeSize.setFixedWidth(60)
        self.EdgeSize.setAlignment(QtCore.Qt.AlignRight)
        self.EdgeSize.setRange(0.1, 5)
        self.EdgeSize.setDecimals(1)
        self.EdgeSize.setSingleStep(0.1)
        self.EdgeSize.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Marker edge width"))
        lyt2.addWidget(self.EdgeSize)
        self.EdgeColor = ColorSelector()
        self.EdgeColor.setToolTip(
            QtWidgets.QApplication.translate("pychemqt", "Marker edge color"))
        lyt2.addWidget(self.EdgeColor)
        layout.addLayout(lyt2)

        self.changeMarker(0)

    def changeMarker(self, index):
        self.MarkerSize.setVisible(index)
        self.MarkerColor.setVisible(index)
        self.EdgeSize.setVisible(index)
        self.EdgeColor.setVisible(index)

    def setConfig(self, config, section="MEOS"):
        alfa = config.getfloat(section, self.conf+"alpha")
        self.Color.setColor(config.get(section, self.conf+'Color'), alfa)
        self.Width.setValue(config.getfloat(section, self.conf+'lineWidth'))
        self.Style.setCurrentValue(config.get(section, self.conf+'lineStyle'))
        self.Marker.setCurrentValue(config.get(section, self.conf+'marker'))
        self.MarkerSize.setValue(
            config.getfloat(section, self.conf+'markersize'))
        self.MarkerColor.setColor(
            config.get(section, self.conf+'markerfacecolor'), alfa)
        self.EdgeSize.setValue(
            config.getfloat(section, self.conf+'markeredgewidth'))
        self.EdgeColor.setColor(
            config.get(section, self.conf+'markeredgecolor'), alfa)

    def value(self, config, section="MEOS"):
        config.set(section, self.conf+"Color", self.Color.color.name())
        config.set(section, self.conf+"alpha", str(self.Color.color.alpha()))
        config.set(section, self.conf+"lineWidth", str(self.Width.value()))
        config.set(section, self.conf+"lineStyle", self.Style.currentValue())
        config.set(section, self.conf+"marker", self.Marker.currentValue())
        config.set(section, self.conf+"markersize",
                   str(self.MarkerSize.value()))
        config.set(section, self.conf+"markerfacecolor",
                   self.MarkerColor.color.name())
        config.set(section, self.conf+"markeredgewidth",
                   str(self.EdgeSize.value()))
        config.set(section, self.conf+"markeredgecolor",
                   self.EdgeColor.color.name())

        return config


class CustomCombo(QtWidgets.QComboBox):
    """General custom QComboBox"""
    valueChanged = QtCore.pyqtSignal("QString")

    def __init__(self, parent=None):
        super(CustomCombo, self).__init__(parent)
        self.setIconSize(QtCore.QSize(35, 18))
        self.currentIndexChanged.connect(self.emit)
        self._populate()

    def setCurrentValue(self, value):
        ind = self.key.index(value)
        self.setCurrentIndex(ind)

    def currentValue(self):
        return self.key[self.currentIndex()]

    def emit(self, ind):
        self.valueChanged.emit(self.key[ind])


class LineStyleCombo(CustomCombo):
    """Custom QComboBox for select matplotlib line styles"""
    key = ["None", "-", "--", ":", "-."]
    image = {
        "None":  "",
        "-": os.path.join("images", "button", "solid_line.png"),
        "--": os.path.join("images", "button", "dash_line.png"),
        ":": os.path.join("images", "button", "dot_line.png"),
        "-.": os.path.join("images", "button", "dash_dot_line.png")}

    def _populate(self):
        for key in self.key:
            self.addItem(QtGui.QIcon(QtGui.QPixmap(
                os.environ["pychemqt"] + self.image[key])), "")


class PFDLineCombo(LineStyleCombo):
    """Custom QComboBox for select PFD line styles for stream"""
    key = [QtCore.Qt.SolidLine, QtCore.Qt.DashLine, QtCore.Qt.DotLine,
           QtCore.Qt.DashDotLine, QtCore.Qt.DashDotDotLine]
    image = {
        key[0]: os.path.join("images", "button", "solid_line.png"),
        key[1]: os.path.join("images", "button", "dash_line.png"),
        key[2]: os.path.join("images", "button", "dot_line.png"),
        key[3]: os.path.join("images", "button", "dash_dot_line.png"),
        key[4]: os.path.join("images", "button", "dash_dot_dot_line.png")}

    valueChanged = QtCore.pyqtSignal(int)


class MarkerCombo(CustomCombo):
    """Custom QComboBox for select matplotlib line marker"""
    key = ["None", ".", ",", "o", "v", "^", "<", ">", "1", "2", "3", "4", "8",
           "s", "p", "*", "h", "H", "+", "x", "D", "d", "|", "_"]
    text = {"None": "", ".": "point", ",": "pixel", "o": "circle",
            "v": "triangle_down", "^": "triangle_up", "<": "triangle_left",
            ">": "triangle_right", "1": "tri_down", "2": "tri_up",
            "3": "tri_left", "4": "tri_right", "8": "octagon", "s": "square",
            "p": "pentagon", "*": "star", "h": "hexagon1", "H": "hexagon2",
            "+": "plus", "x": "x", "D": "diamond", "d": "thin_diamond",
            "|": "vline", "_": "hline"}

    def _populate(self):
        for key in self.key:
            txt = self.text[key]
            if txt:
                image = os.environ["pychemqt"] + \
                    os.path.join("images", "marker", "%s.png" % txt)
                self.addItem(QtGui.QIcon(QtGui.QPixmap(image)), self.text[key])
            else:
                self.addItem(self.text[key])


class NumericFactor(QtWidgets.QDialog):
    """Numeric format configuration dialog"""
    def __init__(self, config, unit=None, order=0, parent=None):
        super(NumericFactor, self).__init__(parent)
        self.setWindowTitle(
            QtWidgets.QApplication.translate("pychemqt", "Format"))
        layout = QtWidgets.QGridLayout(self)
        self.checkFixed = QtWidgets.QRadioButton(
            QtWidgets.QApplication.translate(
                "pychemqt", "Fixed decimal point"))
        layout.addWidget(self.checkFixed, 1, 1, 1, 3)
        layout.addWidget(QtWidgets.QLabel(QtWidgets.QApplication.translate(
            "pychemqt", "Total digits")), 2, 2)
        self.TotalDigits = Entrada_con_unidades(
            int, width=45, value=0, boton=False, spinbox=True, min=0, max=12,
            showNull=True)
        layout.addWidget(self.TotalDigits, 2, 3)
        layout.addWidget(QtWidgets.QLabel(QtWidgets.QApplication.translate(
            "pychemqt", "Decimal digits")), 3, 2)
        self.DecimalDigits = Entrada_con_unidades(
            int, width=45, value=4, boton=False, spinbox=True, min=1, max=12)
        layout.addWidget(self.DecimalDigits, 3, 3)
        self.checkSignificant = QtWidgets.QRadioButton(
            QtWidgets.QApplication.translate(
                "pychemqt", "Significant figures"))
        layout.addWidget(self.checkSignificant, 4, 1, 1, 3)
        layout.addWidget(QtWidgets.QLabel(QtWidgets.QApplication.translate(
            "pychemqt", "Figures")), 5, 2)
        self.FiguresSignificatives = Entrada_con_unidades(
            int, width=45, value=5, boton=False, spinbox=True, min=1, max=12)
        layout.addWidget(self.FiguresSignificatives, 5, 3)
        self.checkExp = QtWidgets.QRadioButton(
            QtWidgets.QApplication.translate(
                "pychemqt", "Exponential preferred"))
        layout.addWidget(self.checkExp, 6, 1, 1, 3)
        layout.addWidget(QtWidgets.QLabel(QtWidgets.QApplication.translate(
            "pychemqt", "Figures")), 7, 2)
        self.FiguresExponential = Entrada_con_unidades(
            int, width=45, value=5, boton=False, spinbox=True, min=1, max=12)
        layout.addWidget(self.FiguresExponential, 7, 3)
        layout.addItem(QtWidgets.QSpacerItem(
            30, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed),
            8, 1)
        self.checkExpVariable = QtWidgets.QCheckBox(
            QtWidgets.QApplication.translate(
                "pychemqt", "Exponential for big/small values"))
        layout.addWidget(self.checkExpVariable, 9, 1, 1, 3)
        self.labelTolerancia = QtWidgets.QLabel(
            QtWidgets.QApplication.translate("pychemqt", "Tolerance"))
        layout.addWidget(self.labelTolerancia, 10, 2)
        self.Tolerance = Entrada_con_unidades(
            int, width=45, value=4, boton=False, spinbox=True, min=0, max=12)
        layout.addWidget(self.Tolerance, 10, 3)
        self.checkSign = QtWidgets.QCheckBox(QtWidgets.QApplication.translate(
            "pychemqt", "Show sign in positive values"))
        layout.addWidget(self.checkSign, 11, 1, 1, 3)
        self.checkThousand = QtWidgets.QCheckBox(
            QtWidgets.QApplication.translate(
                "pychemqt", "Show thousand separator"))
        layout.addWidget(self.checkThousand, 12, 1, 1, 3)

        self.checkFixed.toggled.connect(self.TotalDigits.setNotReadOnly)
        self.checkFixed.toggled.connect(self.DecimalDigits.setNotReadOnly)
        self.checkSignificant.toggled.connect(
            self.FiguresSignificatives.setNotReadOnly)
        self.checkExp.toggled.connect(self.ExpToggled)
        self.checkExp.toggled.connect(self.FiguresExponential.setNotReadOnly)
        self.checkExpVariable.toggled.connect(self.Tolerance.setNotReadOnly)
        self.checkExpVariable.toggled.connect(self.labelTolerancia.setEnabled)

        layout.addItem(QtWidgets.QSpacerItem(
            20, 10, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed),
            13, 1)
        self.muestra = QtWidgets.QLabel()
        layout.addWidget(self.muestra, 14, 1, 1, 3)

        buttonBox = QtWidgets.QDialogButtonBox()
        buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel |
                                     QtWidgets.QDialogButtonBox.Ok)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)
        layout.addWidget(buttonBox, 20, 1, 1, 3)

        self.checkFixed.setChecked(config["format"] == 0)
        self.TotalDigits.setNotReadOnly(config["format"] == 0)
        self.DecimalDigits.setNotReadOnly(config["format"] == 0)
        self.checkSignificant.setChecked(config["format"] == 1)
        self.FiguresSignificatives.setNotReadOnly(config["format"] == 1)
        self.checkExp.setChecked(config["format"] == 2)
        self.FiguresExponential.setNotReadOnly(config["format"] == 2)
        if config["format"] == 0:
            self.DecimalDigits.setValue(config["decimales"])
        elif config["format"] == 1:
            self.FiguresSignificatives.setValue(config["decimales"])
        else:
            self.FiguresExponential.setValue(config["decimales"])
        if "total" in config:
            self.TotalDigits.setValue(config["total"])
        if "exp" in config:
            self.checkExpVariable.setChecked(config["exp"])
        if "tol" in config:
            self.Tolerance.setValue(config["tol"])
        self.Tolerance.setNotReadOnly(config.get("exp", False))
        if "signo" in config:
            self.checkSign.setChecked(config["signo"])
        if "thousand" in config:
            self.checkThousand.setChecked(config["thousand"])

        self.updateMuestra()
        self.checkFixed.toggled.connect(self.updateMuestra)
        self.checkSignificant.toggled.connect(self.updateMuestra)
        self.checkExp.toggled.connect(self.updateMuestra)
        self.checkExpVariable.toggled.connect(self.updateMuestra)
        self.TotalDigits.valueChanged.connect(self.updateMuestra)
        self.DecimalDigits.valueChanged.connect(self.updateMuestra)
        self.FiguresSignificatives.valueChanged.connect(self.updateMuestra)
        self.FiguresExponential.valueChanged.connect(self.updateMuestra)
        self.Tolerance.valueChanged.connect(self.updateMuestra)
        self.checkSign.toggled.connect(self.updateMuestra)
        self.checkThousand.toggled.connect(self.updateMuestra)

        if unit and unit.__text__:
            layout.addItem(QtWidgets.QSpacerItem(
                20, 10, QtWidgets.QSizePolicy.Fixed,
                QtWidgets.QSizePolicy.Fixed), 15, 1, 1, 3)
            self.muestra = QtWidgets.QLabel()
            layout.addWidget(QtWidgets.QLabel(QtWidgets.QApplication.translate(
                "pychemqt", "Convert units")), 16, 1)
            self.unit = QtWidgets.QComboBox()
            for txt in unit.__text__:
                self.unit.addItem(txt)
            self.unit.setCurrentIndex(order)
            layout.addWidget(self.unit, 16, 2, 1, 2)

    def ExpToggled(self, bool):
        self.FiguresExponential.setNotReadOnly(bool)
        self.checkExpVariable.setDisabled(bool)
        if self.checkExpVariable.isChecked():
            self.labelTolerancia.setDisabled(False)
            self.Tolerance.setReadOnly(True)

    def updateMuestra(self):
        kwargs = self.args()
        txt = QtWidgets.QApplication.translate("pychemqt", "Sample") + ": " + \
            representacion(pi*1e4, **kwargs)
        self.muestra.setText(txt)

    def args(self):
        kwarg = {}
        if self.checkFixed.isChecked():
            kwarg["format"] = 0
            kwarg["total"] = self.TotalDigits.value
            kwarg["decimales"] = self.DecimalDigits.value
        elif self.checkSignificant.isChecked():
            kwarg["format"] = 1
            kwarg["decimales"] = self.FiguresSignificatives.value
        else:
            kwarg["format"] = 2
            kwarg["decimales"] = self.FiguresExponential.value
        kwarg["exp"] = self.checkExpVariable.isEnabled() and \
            self.checkExpVariable.isChecked()
        kwarg["tol"] = self.Tolerance.value
        kwarg["signo"] = self.checkSign.isChecked()
        kwarg["thousand"] = self.checkThousand.isChecked()
        return kwarg


class InputFont(QtWidgets.QWidget):
    """Custom widget to edit a text input with font and color support"""
    textChanged = QtCore.pyqtSignal("QString")
    fontChanged = QtCore.pyqtSignal("QFont")
    colorChanged = QtCore.pyqtSignal("QString")

    def __init__(self, text="", font=None, color="#000000", parent=None):
        """
        text: Initial txt for widget
        font: QFont instance to initialize widget
        color: Inicial color to widget, in code #rrggbb
        """
        super(InputFont, self).__init__(parent)

        layout = QtWidgets.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        self.lineEdit = QtWidgets.QLineEdit()
        self.lineEdit.setSizePolicy(
            QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
        layout.addWidget(self.lineEdit)
        self.fontButton = QtWidgets.QPushButton(QtGui.QIcon(QtGui.QPixmap(
            os.environ["pychemqt"] +
            os.path.join("images", "button", "font.png"))), "")
        self.fontButton.setFixedSize(24, 24)
        self.fontButton.setIconSize(QtCore.QSize(24, 24))
        self.fontButton.clicked.connect(self.fontButtonClicked)
        layout.addWidget(self.fontButton)
        self.colorButton = QtWidgets.QToolButton()
        self.colorButton.setFixedSize(24, 24)
        self.colorButton.clicked.connect(self.colorButtonClicked)
        layout.addWidget(self.colorButton)

        self.font = font
        self.setRGB(color)
        self.setText(text)
        self.lineEdit.textChanged.connect(self.textChanged.emit)

    def setText(self, txt):
        self.txt = txt
        self.lineEdit.setText(txt)

    def setRGB(self, rgb):
        """Wrap method to set color with a #rrggbb code"""
        color = QtGui.QColor(rgb)
        if color.isValid():
            self.setColor(color)

    def setColor(self, color):
        self.colorButton.setPalette(QtGui.QPalette(color))
        paleta = QtGui.QPalette()
        paleta.setColor(QtGui.QPalette.Text, color)
        self.lineEdit.setPalette(paleta)
        self.colorChanged.emit(color.name())
        self.color = color

    def setFont(self, font):
        self.font = font
        self.lineEdit.setFont(font)
        self.fontChanged.emit(font)

    def colorButtonClicked(self):
        """Show QColorDialog to change the color"""
        dlg = QtWidgets.QColorDialog(self.color, self)
        if dlg.exec_():
            self.setColor(dlg.currentColor())

    def fontButtonClicked(self):
        """Show QFontDialog to choose the font"""
        dlg = QtWidgets.QFontDialog(self.lineEdit.font())
        if dlg.exec_():
            self.setFont(dlg.currentFont())


class Table_Graphics(QtWidgets.QWidget):
    """Custom widget to implement as popup in PFD when mouse over stream and
    equipment graphic item, to show the status of entity and the properties
    desired if availables"""
    def __init__(self, entity, id, preferences, parent=None):
        super(Table_Graphics, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.Popup)
        layout = QtWidgets.QVBoxLayout(self)
        if isinstance(entity, Corriente):
            title = "Stream %i" % id
        else:
            title = "Equipment %i" % id
        label = QtWidgets.QLabel(title)
        label.setAlignment(QtCore.Qt.AlignCenter)
        layout.addWidget(label)
        line = QtWidgets.QFrame()
        line.setFrameShape(QtWidgets.QFrame.HLine)
        line.setFrameShadow(QtWidgets.QFrame.Sunken)
        layout.addWidget(line)
        if entity:
            if entity.status:
                textos = entity.popup(preferences)
                for txt, tooltip, j in textos:
                    label = QtWidgets.QLabel(txt)
                    label.setToolTip(tooltip)
                    if j:
                        label.setAlignment(QtCore.Qt.AlignRight)
                    layout.addWidget(label)
            else:
                layout.addWidget(QtWidgets.QLabel(entity.msg))
        else:
            layout.addWidget(QtWidgets.QLabel(
                QtWidgets.QApplication.translate("pychemqt", "Undefined")))


def createAction(text, slot=None, shortcut=None, icon=None, tip=None,
                 checkable=False, button=False, parent=None):
    if not tip:
        tip = text
    action = QtWidgets.QAction(text, parent)
    if icon:
        action.setIcon(QtGui.QIcon(IMAGE_PATH + icon))
    if shortcut:
        action.setShortcut(shortcut)
    action.setToolTip(tip)
    action.setStatusTip(tip)
    if slot:
        action.triggered.connect(slot)
    if checkable:
        action.setCheckable(True)

    if button:
        boton = DragButton(parent)

        boton.setIcon(QtGui.QIcon(QtGui.QPixmap(IMAGE_PATH + icon)))
        boton.setToolTip(tip)
        boton.setStatusTip(tip)
        if slot:
            boton.clicked.connect(slot)
        boton.setCheckable(checkable)
        boton.setIconSize(QtCore.QSize(36, 36))
        boton.setFixedSize(QtCore.QSize(36, 36))
        return action, boton
    else:
        return action


def okToContinue(parent, dirty, func, parameters):
    """Function to ask user if any unsaved change
        parent: widget to close
        dirty: boolean to show any unsaved change
        func: function to run if user want to save changes
        parameters: parameter of func"""
    if not dirty:
        return True
    dialog = QtWidgets.QMessageBox.question(
        parent,
        QtWidgets.QApplication.translate("pychemqt", "Unsaved changes"),
        QtWidgets.QApplication.translate("pychemqt", "Save unsaved changes?"),
        QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No |
        QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Yes)
    if dialog == QtWidgets.QMessageBox.Cancel:
        return False
    elif dialog == QtWidgets.QMessageBox.No:
        return True
    elif dialog == QtWidgets.QMessageBox.Yes:
        func(*parameters)
        return True


def mathTex2QPixmap(mathTex, fs):
    """Convert a latex text to a QPixmap to display in any qt widget. Code
    snippet from https://stackoverflow.com/questions/32035251

    Parameters
    ----------
    mathTex : str
        Latex text of expression to show
    fs : int
        Font size of text to show

    Return
    ------
    qpixmap : :class:`QtGui.QPixmap`
        QPixmap ready to show in any other qt widget
    """

    # set up a mpl figure instance
    fig = Figure()
    fig.patch.set_facecolor('none')
    fig.set_canvas(FigureCanvasAgg(fig))
    renderer = fig.canvas.get_renderer()

    # plot the mathTex expression
    ax = fig.add_axes([0, 0, 1, 1])
    ax.axis('off')
    ax.patch.set_facecolor('none')
    t = ax.text(0, 0, mathTex, ha='left', va='bottom', fontsize=fs)

    # fit figure size to text artist
    fwidth, fheight = fig.get_size_inches()
    fig_bbox = fig.get_window_extent(renderer)

    text_bbox = t.get_window_extent(renderer)

    tight_fwidth = text_bbox.width * fwidth / fig_bbox.width
    tight_fheight = text_bbox.height * fheight / fig_bbox.height

    fig.set_size_inches(tight_fwidth, tight_fheight)

    # convert mpl figure to QPixmap
    buf, size = fig.canvas.print_to_buffer()
    qimage = QtGui.QImage(buf, size[0], size[1], QtGui.QImage.Format_ARGB32)
    qpixmap = QtGui.QPixmap(qimage)

    return qpixmap


class QLabelMath(QtWidgets.QLabel):
    """Customized QLabel to show a pixmap with a mathematical formulae"""
    def __init__(self, tex, fs=12, *args, **kw):
        """
        Parameters
        ----------
        tex : str
            Latex code text of mathematical expresion to show
        fs : int
            Font size used in image
        """
        super(QLabelMath, self).__init__(*args, **kw)
        self.setAlignment(QtCore.Qt.AlignCenter)
        self.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.setFrameStyle(QtWidgets.QFrame.Plain)
        pixmap = mathTex2QPixmap(tex, fs)
        self.setPixmap(pixmap)
        self.fs = fs

    def setTex(self, tex):
        pixmap = mathTex2QPixmap(tex, self.fs)
        self.setPixmap(pixmap)


if __name__ == "__main__":
    from lib import unidades

    app = QtWidgets.QApplication(sys.argv)

    ui = QtWidgets.QDialog()
    layout = QtWidgets.QVBoxLayout(ui)

    w = Entrada_con_unidades(unidades.Pressure)
    layout.addWidget(w)
    w2 = ColorSelector(isAlpha=True)
    layout.addWidget(w2)
    w3 = PathConfig()
    layout.addWidget(w3)
    w4 = LineConfig("saturation", "Line Style")
    layout.addWidget(w4)
    w5 = InputFont(text="foo bar", color="#0000ff")
    layout.addWidget(w5)
    w6 = Tabla(columnas=1, filas=1, verticalHeaderModel="C", dinamica=True)
    layout.addWidget(w6)

    ui.show()
    sys.exit(app.exec_())