
from datanodes.core.utils import dumpException
from datanodes.core.main_conf import *
from datanodes.nodes.datanode import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from sklearn.linear_model import LinearRegression
import matplotlib.tri as tri
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.pyplot as plt

LINE_STYLES = ['solid',   'dotted', 'dashed','dashdot', 'solid', 'dotted', 'dashed','dashdot', 'solid', 'dotted', 'dashed','dashdot', 'solid', 'dotted', 'dashed','dashdot', 'dotted', 'dashed','dashdot']
COLORS      = ['#D98880', '#AF7AC5', '#85C1E9', '#6C3483', '#196F3D', '#CB4335', '#58D68D', '#2874A6', '#A2D9CE', '#935116', '#DC7633', '#E59866', '#154360', '#16A085', '#7D6608', '#313131']

class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)



class GraphicsOutputGraphicsNode(ResizebleDataNode):
    def initSizes(self):
        super().initSizes()
        self.width  = 300.0
        self.height = 300.0

class GraphicsOutputContent(DataContent):
    def initUI(self):
        super().initUI()
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        # graph area
        self.graph = Figure()
        self.graph.autofmt_xdate()
        self.axes   = self.graph.add_subplot(111)
        self.canvas = FigureCanvas(self.graph)
        self.layout.addWidget(self.canvas)


class LineStylePicker(QComboBox):
    def __init__(self, node, name, value=None):
        super().__init__()
        self.node = node
        self.name = name
        self.addItem("solid")
        self.addItem("dashed")
        self.addItem("dashdot")
        self.addItem("dotted")
        self.addItem(" ")
        self.addItem("o")
        self.addItem("x")
        self.addItem("s")

        if value is not None:
            index = self.findText(value, Qt.MatchFixedString)
            self.setCurrentIndex(index)
        self.currentIndexChanged.connect(self.chageStyle)

    def chageStyle(self):
        item = self.currentText()
        if item == "solid" or item == "dashed" or item == "dashdot" or item == "dotted":
            self.node.properties.graphtype[self.name] = 'line'
            self.node.properties.linestyle[self.name] = item
            self.node.drawPlot()    
        else:
            self.node.properties.graphtype[self.name] = 'scatter'
            self.node.properties.linestyle[self.name] = item
            self.node.drawPlot()    

class LineSizePicker(QLineEdit):
    def __init__(self, node, name, text):
        super().__init__()
        self.node = node
        self.name = name
        self.setText(str(text))
        self.textChanged.connect(self.chageSize)

    def chageSize(self):
        try:
            self.node.properties.linesize[self.name] = float(self.text())
            self.node.drawPlot()    
        except Exception as e:
            self.node.e = e
            self.node.properties.linesize[self.name] = float(1.0)
    @property
    def value(self):
        return float(self.text())


class ColorPicker(QWidget):
    def __init__(self, node, name, color=None):
        super().__init__()
        self.color = color
        self.node = node
        self.name = name
        self.setAutoFillBackground(True)
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setStyleSheet("background-color:" + self.color + " ;")

    def mouseReleaseEvent(self, event):
        self.color = QColorDialog.getColor()

        if self.color.isValid():
            print(self.color.name())
            self.setStyleSheet("background-color:" + self.color.name() + " ;")

            try:
                self.node.properties.linecolor[self.name] = self.color.name()
                self.node.drawPlot()    
            except Exception as e:
                self.node.e = e


class PlotProperties(NodeProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)
        self.marginTop  = 10
        self.marginRight= 10
        self.marginBottom= 10
        self.marginLeft = 10
        self.labelsize  = 12.0
        self.ticksize   = 10.0
        self.legendsize = 12.0

        label = QLabel("margins ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 1, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        self.marginTopW  = QLineEdit(str(self.marginTop), self)
        self.marginTopW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginTopW, 2, 0, 1, 2)

        self.marginRightW  = QLineEdit(str(self.marginRight), self)
        self.marginRightW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginRightW, 3, 1)

        self.marginLeftW  = QLineEdit(str(self.marginLeft), self)
        self.marginLeftW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginLeftW, 3, 0)

        self.marginBottomW  = QLineEdit(str(self.marginBottom), self)
        self.marginBottomW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginBottomW, 4, 0, 1, 2)
        self.marginBottomW.setStyleSheet("margin-bottom: 15px;")


        label = QLabel("Font sizes ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 5, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("lebels  ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.labelsizeW  = QLineEdit(str(self.labelsize), self)
        self.labelsizeW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 6, 0)
        self.layout.addWidget(self.labelsizeW, 6, 1)

        label = QLabel("ticks   ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ticksizeW  = QLineEdit(str(self.ticksize), self)
        self.ticksizeW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 7, 0)
        self.layout.addWidget(self.ticksizeW, 7, 1)

        label = QLabel("legends ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.legendsizeW  = QLineEdit(str(self.legendsize), self)
        self.legendsizeW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 8, 0)
        self.layout.addWidget(self.legendsizeW, 8, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.legendsizeW.setStyleSheet("margin-bottom: 15px;")

        self.marginTopW.returnPressed.connect(self.updateData)
        self.marginRightW.returnPressed.connect(self.updateData)
        self.marginLeftW.returnPressed.connect(self.updateData)
        self.marginBottomW.returnPressed.connect(self.updateData)
        self.labelsizeW.returnPressed.connect(self.updateData)
        self.ticksizeW.returnPressed.connect(self.updateData)
        self.legendsizeW.returnPressed.connect(self.updateData)

    def updateContent(self):
        self.labelsize    = float(self.labelsizeW.text())
        self.ticksize     = float(self.ticksizeW.text())
        self.legendsize   = float(self.legendsizeW.text())
        self.marginTop    = float(self.marginTopW.text())
        self.marginRight  = float(self.marginRightW.text())
        self.marginLeft   = float(self.marginLeftW.text())
        self.marginBottom = float(self.marginBottomW.text())

    def updateData(self):
        self.updateContent()
        self.node.drawPlot()

    def fillWidgets(self):
        self.marginTopW.setText(str(self.marginTop))
        self.marginRightW.setText(str(self.marginRight))
        self.marginLeftW.setText(str(self.marginLeft))
        self.marginBottomW.setText(str(self.marginBottom))
        self.labelsizeW.setText(str(self.labelsize))
        self.ticksizeW.setText(str(self.ticksize))
        self.legendsizeW.setText(str(self.legendsize))

    def serialize(self):
        res = super().serialize()
        res['margintop']   = self.marginTop
        res['marginright'] = self.marginRight
        res['marginleft']  = self.marginLeft
        res['marginbottom']= self.marginBottom
        res['labelsize']   = self.labelsize
        res['ticksize']    = self.ticksize
        res['legendsize']  = self.legendsize

        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                if 'margintop'    in data : self.marginTop    = data['margintop']  
                if 'marginright'  in data : self.marginRight  = data['marginright'] 
                if 'marginleft'   in data : self.marginLeft   = data['marginleft'] 
                if 'marginbottom' in data : self.marginBottom = data['marginbottom'] 
                if 'labelsize'   in data : self.labelsize = data['labelsize']
                if 'ticksize'    in data : self.ticksize  = data['ticksize']
                if 'legendsize'  in data : self.legendsize  = data['legendsize']

                self.fillWidgets()

            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res















class GraphicsProperties(PlotProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)
        self.xtitle    = ""
        self.ytitle    = ""
        self.names     = {}
        self.linesize  = {}
        self.linecolor = {}
        self.linestyle = {}
        self.graphtype = {}
        self.c = 0

        self.labels    = {}
        self.styles    = {}
        self.sizes     = {}
        self.colors    = {}

        label = QLabel("Axis names ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 9, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("x name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.xlabelW  = QLineEdit(self.xtitle, self)
        self.xlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 10, 0)
        self.layout.addWidget(self.xlabelW, 10, 1)

        label = QLabel("y name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ylabelW  = QLineEdit(self.ytitle, self)
        self.ylabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 11, 0)
        self.layout.addWidget(self.ylabelW, 11, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.ylabelW.setStyleSheet("margin-bottom: 15px;")

        self.xlabelW.returnPressed.connect(self.updateData)
        self.ylabelW.returnPressed.connect(self.updateData)


    def updateContent(self):
        super().updateContent()
        self.xtitle       = self.xlabelW.text()
        self.ytitle       = self.ylabelW.text()


    def fillWidgets(self):
        super().fillWidgets()
        self.xlabelW.setText(self.xtitle)
        self.ylabelW.setText(self.ytitle)
        self.appendDataWidgets()

    def appendDataWidgets(self):
        self.i = 12
        for name in self.names : 

            self.labels[name] = QLabel(name + " ", self)        
            self.labels[name].setAlignment(Qt.AlignRight | Qt.AlignVCenter)

            self.styles[name] = LineStylePicker(self.node, name, self.linestyle[name])

            self.labels[name].setStyleSheet("margin-top: 15px;")
            self.styles[name].setStyleSheet("margin-top: 15px;")

            self.layout.addWidget(self.labels[name], self.i, 0)
            self.layout.addWidget(self.styles[name], self.i, 1)
            self.i += 1


            self.sizes[name]  = LineSizePicker(self.node, name, self.linesize[name])
            self.sizes[name].setAlignment(Qt.AlignLeft | Qt.AlignVCenter)

            self.colors[name] = ColorPicker(self.node, name, color=self.linecolor[name])


            self.layout.addWidget(self.sizes[name],  self.i, 0)
            self.layout.addWidget(self.colors[name], self.i, 1)
            self.i += 1

    def resetWidgets(self):
        self.resetProperties()
        self.names     = {}
        self.linesize  = {}
        self.linecolor = {}
        self.linestyle = {}
        self.graphtype  = {}
        self.c = 0

    def cleanProperties(self):
        for input in self.node.inputs:
            if not input.value : continue

            value  = input.value
            x_name = list(value.keys())[0]

            for name in value:
                if name != x_name:
                    if name not in self.names : 
                        self.names[name]     = name
                        self.linecolor[name] = COLORS[self.c]
                        self.linestyle[name] = "solid"
                        self.graphtype[name] = "line"
                        self.linesize[name]  = 2.0
                        self.c += 1

        for name in self.names:
            if name != x_name:
                if name in self.labels:
                    self.layout.removeWidget(self.labels[name])
                    self.layout.removeWidget(self.styles[name])
                    self.layout.removeWidget(self.sizes[name])
                    self.layout.removeWidget(self.colors[name])

                    self.labels[name].setParent(None)
                    self.styles[name].setParent(None)
                    self.sizes[name].setParent(None)
                    self.colors[name].setParent(None)
                    
                    del self.labels[name]
                    del self.styles[name]
                    del self.sizes[name]
                    del self.colors[name]


        remove = []
        for name in reversed(self.names):
            to_remove = True
            for input in self.node.inputs:
                if name in input.value: 
                    to_remove = False
            if to_remove: remove.append(name)

        for name in remove:
            del self.names[name]
            del self.linecolor[name]
            del self.linestyle[name]
            del self.graphtype[name]
            del self.linesize[name]


    def serialize(self):
        res = super().serialize()
        res['xtitle']     = self.xtitle
        res['ytitle']     = self.ytitle
        """
        styles  = []
        colors  = []
        types   = []
        names   = []
        sizes   = []

        for name in self.names:
            styles.append(self.linestyle[name])
            colors.append(self.linecolor[name])
            types.append( self.graphtype[name])
            sizes.append( self.linesize[name])
            names.append(name)

        res['styles'] = styles
        res['colors'] = colors
        res['types']  = types
        res['sizes']  = sizes
        res['names']  = names
        """
        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                #self.resetWidgets()
                if 'xtitle'      in data : self.xtitle = data['xtitle']
                if 'ytitle'      in data : self.ytitle = data['ytitle']
                """
                for i, name in enumerate(data['names']):
                    self.names[name]     = name
                    self.linecolor[name] = data['colors'][i]
                    self.linestyle[name] = data['styles'][i]
                    self.graphtype[name] = data['types'][i]
                    self.linesize[name]  = data['sizes'][i]

                    self.c += 1
                """
                self.fillWidgets()
            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res



@register_node(OP_MODE_PLOT)
class GraphicsOutputNode(ResizableInputNode):
    icon = "icons/valoutput.png"
    op_code = OP_MODE_PLOT
    op_title = "Plot"

    def __init__(self, scene, inputs=[1], outputs=[]):
        super().__init__(scene, inputs, outputs)
        self.insockets    = []
        self.linecolor = {}
        self.linestyle = {}
        self.graphtype = {}
        self.linesize  = {}
        self.c = 0
        self.input_socket_position  = LEFT_TOP

    def onInputChange(self, new_edge=None):
        self.content.changed.emit()

    def resize(self):
        pass

    def initInnerClasses(self):
        self.content    = GraphicsOutputContent(self)
        self.grNode     = GraphicsOutputGraphicsNode(self)
        self.properties = GraphicsProperties(self)
        self.content.changed.connect(self.recalculateNode)


    def prepareSettings(self):
        #self.properties.resetProperties()
        self.properties.cleanProperties()
        self.properties.fillWidgets()
        return True

    def prepareCanvas(self):
        if not self.hasValue(0) : return

        value = self.getInput(0).value
        self.content.axes.clear()
        x_name = list(value.keys())[0]
        self.x_val  = value[x_name]

        if self.properties.xtitle == "":
            self.content.axes.set_xlabel(x_name)
        else:
            self.content.axes.set_xlabel(self.properties.xtitle)

        self.content.axes.set_ylabel(self.properties.ytitle)
        self.content.axes.xaxis.label.set_size( self.properties.labelsize )
        self.content.axes.yaxis.label.set_size( self.properties.labelsize )

        self.content.axes.tick_params(labelsize= self.properties.ticksize)
        self.content.axes.tick_params(labelsize= self.properties.ticksize)

    def addData(self, value):
        if not value : return 

        x_name = list(value.keys())[0]
        x_val  = value[x_name]
        type = 'line'

        for i, name in enumerate(value):
            if name != x_name:
                if self.properties.graphtype[name] == 'line':
                    self.content.axes.plot(x_val, value[name], label=name, 
                                            color=self.properties.linecolor[name], linestyle=self.properties.linestyle[name],
                                            linewidth=self.properties.linesize[name])

                if self.properties.graphtype[name] == 'scatter':
                    self.content.axes.scatter(x_val, value[name], label=name, 
                                            color=self.properties.linecolor[name], marker=self.properties.linestyle[name],
                                            s=self.properties.linesize[name]*10)

    def drawPlot(self):
        self.content.axes.clear()
        self.prepareCanvas()
        for input in self.insockets:
            self.addData(input.value)
        self.content.axes.legend(loc = 1, fontsize=self.properties.legendsize)
        self.content.canvas.draw()


    def evalImplementation(self, silent=False):
        self.insockets = self.getInputs()
        if not self.insockets:
            self.setInvalid()
            self.e = "Does not have and intry Node"
            return False
        else:
            self.sortSockets()
            if len(self.insockets) > 0:
                self.setDirty(False)
                self.setInvalid(False)
                self.e = ""
                self.value = {}

                self.drawPlot()

                return True
            else:
                self.setDirty(False)
                self.setInvalid(False)
                self.e = "Not enough input data"
                self.getOutput(0).value = 0
                self.getOutput(0).type = "float"
                return False










class TernaryPlotCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig  = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.fig.add_axes([0.1, 0.1,  0.75, 0.85])
        self.bar  = self.fig.add_axes([0.9, 0.1,  0.05, 0.85])
        super(TernaryPlotCanvas, self).__init__(self.fig)

class TernaryPlotGraphicsNode(ResizebleDataNode):
    def initSizes(self):
        super().initSizes()
        self.width  = 300.0
        self.height = 300.0

class TernaryPlotContent(DataContent):
    def initUI(self):
        super().initUI()
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        self.canvas = TernaryPlotCanvas()
        self.layout.addWidget(self.canvas)


    def serialize(self):
        res = super().serialize()
        res['width'] = self.node.grNode.width
        res['height'] = self.node.grNode.height
        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                self.node.grNode.height = data['height']
                self.node.grNode.width  = data['width']
                self.updateSize()
            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res

class TernaryPlotProperties(PlotProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)

        self.levels     = 10
        self.barfont    = 12
        self.xtitle     = ""
        self.ytitle     = ""
        self.ztitle     = ""

        label = QLabel("levels ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.levelsW  = QLineEdit(str(self.levels), self)
        self.levelsW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 9, 0)
        self.layout.addWidget(self.levelsW, 9, 1)
        label.setStyleSheet("margin-bottom: 5px;")
        self.levelsW.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("bar font size ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.barfontW  = QLineEdit(str(self.barfont), self)
        self.barfontW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 10, 0)
        self.layout.addWidget(self.barfontW, 10, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.barfontW.setStyleSheet("margin-bottom: 15px;")


        label = QLabel("Axis names ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 11, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("x name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.xlabelW  = QLineEdit(self.xtitle, self)
        self.xlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 12, 0)
        self.layout.addWidget(self.xlabelW, 12, 1)

        label = QLabel("y name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ylabelW  = QLineEdit(self.ytitle, self)
        self.ylabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 13, 0)
        self.layout.addWidget(self.ylabelW, 13, 1)

        label = QLabel("z name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.zlabelW  = QLineEdit(self.ztitle, self)
        self.zlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 14, 0)
        self.layout.addWidget(self.zlabelW, 14, 1)

        label.setStyleSheet("margin-bottom: 15px;")
        self.zlabelW.setStyleSheet("margin-bottom: 15px;")

        self.levelsW.returnPressed.connect(self.updateData)
        self.barfontW.returnPressed.connect(self.updateData)
        self.xlabelW.returnPressed.connect(self.updateData)
        self.ylabelW.returnPressed.connect(self.updateData)
        self.zlabelW.returnPressed.connect(self.updateData)


    def updateContent(self):
        super().updateContent()
        self.levels       = int(self.levelsW.text())
        self.barfont      = float(self.barfontW.text())
        self.xtitle       = self.xlabelW.text()
        self.ytitle       = self.ylabelW.text()
        self.ztitle       = self.zlabelW.text()

    def fillWidgets(self):
        super().fillWidgets()
        self.levelsW.setText(str(self.levels))
        self.xlabelW.setText(self.xtitle)
        self.ylabelW.setText(self.ytitle)
        self.zlabelW.setText(self.ztitle)
        self.barfontW.setText(str(self.barfont))

    def serialize(self):
        res = super().serialize()
        res['barfont']    = self.barfont
        res['levels']     = self.levels
        res['xtitle']     = self.xtitle
        res['ytitle']     = self.ytitle
        res['ztitle']     = self.ztitle

        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                if 'levels'      in data : self.levels  = data['levels']
                if 'xtitle'      in data : self.xtitle  = data['xtitle']
                if 'ytitle'      in data : self.ytitle  = data['ytitle']
                if 'ztitle'      in data : self.ztitle  = data['ztitle']
                if 'barfont'     in data : self.barfont = data['barfont']

                self.fillWidgets()

            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res


@register_node(OP_MODE_PLOT_TERNARY)
class TernaryPlotNode(ResizableInputNode):
    icon = "icons/valoutput.png"
    op_code = OP_MODE_PLOT_TERNARY
    op_title = "Ternary Diagram"

    def __init__(self, scene, inputs=[1], outputs=[]):
        super().__init__(scene, inputs, outputs)
        self.vmax = 1.0
        self.vmin = 0.0
        self.cmap = plt.cm.viridis
        self.insockets = []

    def initInnerClasses(self):
        self.content = TernaryPlotContent(self)
        self.grNode  = TernaryPlotGraphicsNode(self)
        self.properties = TernaryPlotProperties(self)
        self.content.changed.connect(self.recalculateNode)

    def prepareSettings(self):
        return True

    def resize(self):
        pass

    def cAxes(self, ax, x, y, pos='x'):
        xmin = x[0]
        xmax = x[-1]
        ymin = y[0]
        ymax = y[-1]
        ax.plot(x, y, color='black')
        
        xt = xmin
        yt = ymin
        dx = (xmax - xmin) / 10
        dy = (ymax - ymin) / 10
        x2 = ( dx * np.cos(1.5708) - dy * np.sin(1.5708) ) / 5 
        y2 = ( dy * np.cos(1.5708) + dx * np.sin(1.5708) ) / 5 
        for i in range(0, 11):
            ax.plot([xt, xt+x2], [yt, yt+y2], color='black')
            ax.text(xt + 1.75*x2, yt + 1.75*y2, i/10,
                    horizontalalignment='center',
                    verticalalignment='center',
                    color='black', fontsize=self.properties.ticksize)
            xt = xt + dx
            yt = yt + dy
        
        
        
    def mesh(self, num):
        # creating mesh 
        sc = 0.5 * np.sqrt(3) * 1.0
        dx = 1.0 / (num)
        dy = 1.0 / (num)
        size = int((num+1) * ((num+1) + 1) / 2)
        xm = np.zeros(size)
        ym = np.zeros(size)
        index = 0
        for i in range(0, (num+1)):
            for n in range(0, i+1):
                xm[index] = 0.5 - 0.5 * i / num + 1.0 * n / num
                ym[index] = (1.0 - dy * i) * sc
                index = index + 1
        
        return np.array([xm, ym]).transpose()
    

    def drawTernary(self, value):
        self.content.canvas.axes.clear()
        if not value : return
        names = list(value.keys())

        # barycentric coords: (a,b,c)
        self.a = np.array( value[names[0]] )
        self.b = np.array( value[names[1]] )
        self.c = np.array( value[names[2]] )
    
        # values is stored in the last column
        self.v = np.array( value[names[-1]] )
        self.vmin = self.v.min()
        self.vmax = self.v.max()

        # translate the data to cartesian corrds
        self.x = 0.5 * ( 2. * self.b + self.c ) / ( self.a + self.b + self.c )
        self.y = 0.5 * np.sqrt(3) * self.c / (self.a + self.b + self.c)
        ylim   = 0.5 * np.sqrt(3) * 1.0
            
        #add frame
        self.cAxes(self.content.canvas.axes, [0, 0.5], [0, ylim])
        self.cAxes(self.content.canvas.axes, [1, 0],   [0, 0])
        self.cAxes(self.content.canvas.axes, [0.5, 1.0], [ylim, 0])
        
        # plot the contour
        im = self.content.canvas.axes.tricontourf(self.x, self.y, self.v, 
                                                  cmap=self.cmap, 
                                                  vmin=self.vmin, vmax=self.vmax,
                                                  levels=self.properties.levels)
        self.content.canvas.bar.set
        self.content.canvas.bar.set_title(names[-1], fontsize = self.properties.barfont)
        self.content.canvas.bar.tick_params(labelsize = self.properties.barfont)
        plt.colorbar(im, cax=self.content.canvas.bar)
        
        
        #position the axis labels
        self.content.canvas.axes.text(1.05, -0.05,  self.properties.ytitle, fontsize=self.properties.labelsize, ha="left")
        self.content.canvas.axes.text(0.50,  0.92,  self.properties.ztitle, fontsize=self.properties.labelsize, ha="center")
        self.content.canvas.axes.text(-0.05,-0.05,  self.properties.xtitle, fontsize=self.properties.labelsize, ha="right")

    def addScatter(self, value):
        names = list(value.keys())

        # barycentric coords: (a,b,c)
        self.a = np.array( value[names[0]] )
        self.b = np.array( value[names[1]] )
        self.c = np.array( value[names[2]] )
    
        # values is stored in the last column
        self.v = np.array( value[names[-1]] )

        # translate the data to cartesian corrds
        self.x = 0.5 * ( 2. * self.b + self.c ) / ( self.a + self.b + self.c )
        self.y = 0.5 * np.sqrt(3) * self.c / (self.a + self.b + self.c)
        ylim   = 0.5 * np.sqrt(3) * 1.0
                    
        # plot the contour
        self.content.canvas.axes.scatter(self.x, self.y, s=200, c=self.v, 
                                         cmap=self.cmap, vmin=self.vmin, vmax=self.vmax, 
                                         edgecolors='black', linewidths=1.0)

    def drawPlot(self):
        self.drawTernary(self.insockets[0].value)
        if len(self.insockets) > 1:
            for input in self.insockets[1:]:
                if input.value:
                    self.addScatter(input.value)

        self.content.canvas.axes.axes.tick_params(labelsize= self.properties.ticksize)

        sc_w = (1.0 - self.properties.marginRight  * 0.01 - self.properties.marginLeft * 0.01)
        sc_h = (1.0 - self.properties.marginBottom * 0.01 - self.properties.marginTop  * 0.01)

        self.content.canvas.bar.set_position([
                            self.properties.marginLeft * 0.01 + sc_w * 0.95, 
                            self.properties.marginBottom * 0.01 + 0.10, 
                            sc_w * 0.05,
                            sc_h * 0.85])

        self.content.canvas.axes.set_position([
                            self.properties.marginLeft * 0.01, 
                            self.properties.marginBottom * 0.01, 
                            sc_w * 0.9, 
                            sc_h])
                    
        self.content.canvas.axes.set_axis_off()
        self.content.canvas.draw()


    def evalImplementation(self, silent=False):
        self.insockets = self.getInputs()
        if not self.insockets:
            if DEBUG : print("OUTNODE_TXT: no input edge")
            self.setInvalid()
            if DEBUG : print("OUTNODE_TXT: set invalid")
            self.e = "Does not have and intry Node"
            if DEBUG : print("OUTNODE_TXT: clear the content")
            return False
        else:            
            if DEBUG : print("OUTNODE_TXT: process the input edge data")
            self.setDirty(False)
            self.setInvalid(False)
            if DEBUG : print("OUTNODE_TXT: reset Dirty and Invalid")
            self.e = ""
            self.value = self.insockets[0].value
            self.drawPlot()

        return True












class HeatMapCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig  = Figure(figsize=(width, height), dpi=dpi, tight_layout=True)
        self.axes = self.fig.add_axes([0.1,  0.1, 0.75, 0.85])
        self.bar  = self.fig.add_axes([0.90, 0.1, 0.05, 0.75])

        super(HeatMapCanvas, self).__init__(self.fig)

class HeatMapGraphicsNode(ResizebleDataNode):
    def initSizes(self):
        super().initSizes()
        self.width  = 300.0
        self.height = 300.0

class HeatMapContent(DataContent):
    def initUI(self):
        super().initUI()
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        self.canvas = HeatMapCanvas()
        self.layout.addWidget(self.canvas)


    def serialize(self):
        res = super().serialize()
        res['width']  = self.node.grNode.width
        res['height'] = self.node.grNode.height
        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                self.node.grNode.height = data['height']
                self.node.grNode.width  = data['width']
                self.updateSize()
            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res

class HeatMapProperties(PlotProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)

        self.levels     = 10
        self.xtitle     = ""
        self.ytitle     = ""

        label = QLabel("levels ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.levelsW  = QLineEdit(str(self.levels), self)
        self.levelsW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 9, 0)
        self.layout.addWidget(self.levelsW, 9, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.levelsW.setStyleSheet("margin-bottom: 15px;")

        label = QLabel("Axis names ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 10, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("x name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.xlabelW  = QLineEdit(self.xtitle, self)
        self.xlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 11, 0)
        self.layout.addWidget(self.xlabelW, 11, 1)

        label = QLabel("y name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ylabelW  = QLineEdit(self.ytitle, self)
        self.ylabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 12, 0)
        self.layout.addWidget(self.ylabelW, 12, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.ylabelW.setStyleSheet("margin-bottom: 15px;")

        self.levelsW.returnPressed.connect(self.updateData)
        self.xlabelW.returnPressed.connect(self.updateData)
        self.ylabelW.returnPressed.connect(self.updateData)


    def updateContent(self):
        super().updateContent()
        self.levels       = int(self.levelsW.text())
        self.xtitle       = self.xlabelW.text()
        self.ytitle       = self.ylabelW.text()

    def fillWidgets(self):
        super().fillWidgets()
        self.levelsW.setText(str(self.levels))
        self.xlabelW.setText(self.xtitle)
        self.ylabelW.setText(self.ytitle)

    def serialize(self):
        res = super().serialize()

        res['levels']     = self.levels
        res['xtitle']     = self.xtitle
        res['ytitle']     = self.ytitle

        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                if 'levels'      in data : self.levels = data['levels']
                if 'xtitle'      in data : self.xtitle = data['xtitle']
                if 'ytitle'      in data : self.ytitle = data['ytitle']

                self.fillWidgets()

            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res



@register_node(OP_MODE_PLOT_HEATMAP)
class HeatMapNode(ResizableInputNode):
    icon = "icons/valoutput.png"
    op_code = OP_MODE_PLOT_HEATMAP
    op_title = "Heat Map"

    def __init__(self, scene, inputs=[1], outputs=[]):
        super().__init__(scene, inputs, outputs)
        self.vmax = 1.0
        self.vmin = 0.0
        self.cmap = plt.cm.viridis
        self.insockets = []

    def initInnerClasses(self):
        self.content = HeatMapContent(self)
        self.grNode  = HeatMapGraphicsNode(self)
        self.properties = HeatMapProperties(self)
        self.content.changed.connect(self.recalculateNode)

    def prepareSettings(self):
        """
        self.properties.resetProperties()
        self.properties.cleanProperties()
        self.properties.fillWidgets()
        """
        return True

    def prepareSettings(self):
        return True

    def resize(self):
        pass

    def addMap(self, value):
        self.content.canvas.axes.clear()
        if not value : return
        names = list(value.keys())

        # barycentric coords: (a,b,c)
        self.x = np.array( value[names[0]] )
        self.y = np.array( value[names[1]] )
    
        # values is stored in the last column
        self.z = np.array( value[names[-1]] )
        self.vmin = self.z.min()
        self.vmax = self.z.max()

        # plot the contour
        im = self.content.canvas.axes.tricontourf(self.x, self.y, self.z, 
                                                  cmap=self.cmap, vmin=self.vmin, vmax=self.vmax,
                                                  levels=self.properties.levels)
        self.content.canvas.bar.set_title(names[-1])
        plt.colorbar(im, cax=self.content.canvas.bar)
        self.content.canvas.axes.set_xlabel(self.properties.xtitle, fontsize=self.properties.labelsize)
        self.content.canvas.axes.set_ylabel(self.properties.ytitle, fontsize=self.properties.labelsize)
        
        self.content.canvas.axes.axes.tick_params(labelsize= self.properties.ticksize)
        self.content.canvas.axes.axes.tick_params(labelsize= self.properties.ticksize)

        sc_w = (1.0 - self.properties.marginRight  * 0.01 - self.properties.marginLeft * 0.01)
        sc_h = (1.0 - self.properties.marginBottom * 0.01 - self.properties.marginTop  * 0.01)

        self.content.canvas.axes.set_position([
                            self.properties.marginLeft * 0.01, 
                            self.properties.marginBottom * 0.01, 
                            sc_w * 0.9, 
                            sc_h])

        self.content.canvas.bar.set_position([
                            self.properties.marginLeft * 0.01 + sc_w * 0.95, 
                            self.properties.marginBottom * 0.01, 
                            sc_w * 0.05,
                            sc_h * 0.90])

        """
        self.axes = self.fig.add_axes([0.1,  0.1, 0.75, 0.85])
        self.bar  = self.fig.add_axes([0.90, 0.1, 0.05, 0.75])

        self.content.canvas.axes.text(1.05, -0.05,  names[1], fontsize=24, ha="left")
        self.content.canvas.axes.text(0.50,  0.92,  names[2], fontsize=24, ha="center")
        self.content.canvas.axes.text(-0.05,-0.05,  names[0], fontsize=24, ha="right")
        """
        

    def drawPlot(self):
        self.insockets = self.getInputs()

        for input in self.insockets:
            if input.value:
                self.addMap(input.value)
        self.content.canvas.draw()


    def evalImplementation(self, silent=False):
        self.insockets = self.getInputs()
        try:
            if not self.insockets:
                if DEBUG : print("OUTNODE_TXT: no input edge")
                self.setInvalid()
                if DEBUG : print("OUTNODE_TXT: set invalid")
                self.e = "Does not have and intry Node"
                if DEBUG : print("OUTNODE_TXT: clear the content")
                return False
            else:            
                if DEBUG : print("OUTNODE_TXT: process the input edge data")
                self.setDirty(False)
                self.setInvalid(False)
                if DEBUG : print("OUTNODE_TXT: reset Dirty and Invalid")
                self.e = ""
                self.drawPlot()

            return True
        except Exception as e:
            self.setDirty(False)
            self.setInvalid(False)
            self.e = e
            return False


from datanodes.core.utils import dumpException
from datanodes.core.main_conf import *
from datanodes.nodes.datanode import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from sklearn.linear_model import LinearRegression
import matplotlib.tri as tri
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.pyplot as plt

LINE_STYLES = ['solid',   'dotted', 'dashed','dashdot', 'solid', 'dotted', 'dashed','dashdot', 'solid', 'dotted', 'dashed','dashdot', 'solid', 'dotted', 'dashed','dashdot', 'dotted', 'dashed','dashdot']
COLORS      = ['#D98880', '#AF7AC5', '#85C1E9', '#6C3483', '#196F3D', '#CB4335', '#58D68D', '#2874A6', '#A2D9CE', '#935116', '#DC7633', '#E59866', '#154360', '#16A085', '#7D6608', '#313131']

class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super(MplCanvas, self).__init__(fig)



class GraphicsOutputGraphicsNode(ResizebleDataNode):
    def initSizes(self):
        super().initSizes()
        self.width  = 300.0
        self.height = 300.0

class GraphicsOutputContent(DataContent):
    def initUI(self):
        super().initUI()
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        # graph area
        self.graph = Figure()
        self.graph.autofmt_xdate()
        self.axes   = self.graph.add_subplot(111)
        self.canvas = FigureCanvas(self.graph)
        self.layout.addWidget(self.canvas)


class LineStylePicker(QComboBox):
    def __init__(self, node, name, value=None):
        super().__init__()
        self.node = node
        self.name = name
        self.addItem("solid")
        self.addItem("dashed")
        self.addItem("dashdot")
        self.addItem("dotted")
        self.addItem(" ")
        self.addItem("o")
        self.addItem("x")
        self.addItem("s")

        if value is not None:
            index = self.findText(value, Qt.MatchFixedString)
            self.setCurrentIndex(index)
        self.currentIndexChanged.connect(self.chageStyle)

    def chageStyle(self):
        item = self.currentText()
        if item == "solid" or item == "dashed" or item == "dashdot" or item == "dotted":
            self.node.properties.graphtype[self.name] = 'line'
            self.node.properties.linestyle[self.name] = item
            self.node.drawPlot()    
        else:
            self.node.properties.graphtype[self.name] = 'scatter'
            self.node.properties.linestyle[self.name] = item
            self.node.drawPlot()    

class LineSizePicker(QLineEdit):
    def __init__(self, node, name, text):
        super().__init__()
        self.node = node
        self.name = name
        self.setText(str(text))
        self.textChanged.connect(self.chageSize)

    def chageSize(self):
        try:
            self.node.properties.linesize[self.name] = float(self.text())
            self.node.drawPlot()    
        except Exception as e:
            self.node.e = e
            self.node.properties.linesize[self.name] = float(1.0)
    @property
    def value(self):
        return float(self.text())


class ColorPicker(QWidget):
    def __init__(self, node, name, color=None):
        super().__init__()
        self.color = color
        self.node = node
        self.name = name
        self.setAutoFillBackground(True)
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setStyleSheet("background-color:" + self.color + " ;")

    def mouseReleaseEvent(self, event):
        self.color = QColorDialog.getColor()

        if self.color.isValid():
            print(self.color.name())
            self.setStyleSheet("background-color:" + self.color.name() + " ;")

            try:
                self.node.properties.linecolor[self.name] = self.color.name()
                self.node.drawPlot()    
            except Exception as e:
                self.node.e = e


class PlotProperties(NodeProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)
        self.marginTop  = 10
        self.marginRight= 10
        self.marginBottom= 10
        self.marginLeft = 10
        self.labelsize  = 12.0
        self.ticksize   = 10.0
        self.legendsize = 12.0

        label = QLabel("margins ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 1, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        self.marginTopW  = QLineEdit(str(self.marginTop), self)
        self.marginTopW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginTopW, 2, 0, 1, 2)

        self.marginRightW  = QLineEdit(str(self.marginRight), self)
        self.marginRightW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginRightW, 3, 1)

        self.marginLeftW  = QLineEdit(str(self.marginLeft), self)
        self.marginLeftW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginLeftW, 3, 0)

        self.marginBottomW  = QLineEdit(str(self.marginBottom), self)
        self.marginBottomW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(self.marginBottomW, 4, 0, 1, 2)
        self.marginBottomW.setStyleSheet("margin-bottom: 15px;")


        label = QLabel("Font sizes ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 5, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("lebels  ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.labelsizeW  = QLineEdit(str(self.labelsize), self)
        self.labelsizeW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 6, 0)
        self.layout.addWidget(self.labelsizeW, 6, 1)

        label = QLabel("ticks   ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ticksizeW  = QLineEdit(str(self.ticksize), self)
        self.ticksizeW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 7, 0)
        self.layout.addWidget(self.ticksizeW, 7, 1)

        label = QLabel("legends ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.legendsizeW  = QLineEdit(str(self.legendsize), self)
        self.legendsizeW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 8, 0)
        self.layout.addWidget(self.legendsizeW, 8, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.legendsizeW.setStyleSheet("margin-bottom: 15px;")

        self.marginTopW.returnPressed.connect(self.updateData)
        self.marginRightW.returnPressed.connect(self.updateData)
        self.marginLeftW.returnPressed.connect(self.updateData)
        self.marginBottomW.returnPressed.connect(self.updateData)
        self.labelsizeW.returnPressed.connect(self.updateData)
        self.ticksizeW.returnPressed.connect(self.updateData)
        self.legendsizeW.returnPressed.connect(self.updateData)

    def updateContent(self):
        self.labelsize    = float(self.labelsizeW.text())
        self.ticksize     = float(self.ticksizeW.text())
        self.legendsize   = float(self.legendsizeW.text())
        self.marginTop    = float(self.marginTopW.text())
        self.marginRight  = float(self.marginRightW.text())
        self.marginLeft   = float(self.marginLeftW.text())
        self.marginBottom = float(self.marginBottomW.text())

    def updateData(self):
        self.updateContent()
        self.node.drawPlot()

    def fillWidgets(self):
        self.marginTopW.setText(str(self.marginTop))
        self.marginRightW.setText(str(self.marginRight))
        self.marginLeftW.setText(str(self.marginLeft))
        self.marginBottomW.setText(str(self.marginBottom))
        self.labelsizeW.setText(str(self.labelsize))
        self.ticksizeW.setText(str(self.ticksize))
        self.legendsizeW.setText(str(self.legendsize))

    def serialize(self):
        res = super().serialize()
        res['margintop']   = self.marginTop
        res['marginright'] = self.marginRight
        res['marginleft']  = self.marginLeft
        res['marginbottom']= self.marginBottom
        res['labelsize']   = self.labelsize
        res['ticksize']    = self.ticksize
        res['legendsize']  = self.legendsize

        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                if 'margintop'    in data : self.marginTop    = data['margintop']  
                if 'marginright'  in data : self.marginRight  = data['marginright'] 
                if 'marginleft'   in data : self.marginLeft   = data['marginleft'] 
                if 'marginbottom' in data : self.marginBottom = data['marginbottom'] 
                if 'labelsize'   in data : self.labelsize = data['labelsize']
                if 'ticksize'    in data : self.ticksize  = data['ticksize']
                if 'legendsize'  in data : self.legendsize  = data['legendsize']

                self.fillWidgets()

            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res















class GraphicsProperties(PlotProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)
        self.xtitle    = ""
        self.ytitle    = ""
        self.names     = {}
        self.linesize  = {}
        self.linecolor = {}
        self.linestyle = {}
        self.graphtype = {}
        self.c = 0

        self.labels    = {}
        self.styles    = {}
        self.sizes     = {}
        self.colors    = {}

        label = QLabel("Axis names ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 9, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("x name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.xlabelW  = QLineEdit(self.xtitle, self)
        self.xlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 10, 0)
        self.layout.addWidget(self.xlabelW, 10, 1)

        label = QLabel("y name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ylabelW  = QLineEdit(self.ytitle, self)
        self.ylabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 11, 0)
        self.layout.addWidget(self.ylabelW, 11, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.ylabelW.setStyleSheet("margin-bottom: 15px;")

        self.xlabelW.returnPressed.connect(self.updateData)
        self.ylabelW.returnPressed.connect(self.updateData)


    def updateContent(self):
        super().updateContent()
        self.xtitle       = self.xlabelW.text()
        self.ytitle       = self.ylabelW.text()


    def fillWidgets(self):
        super().fillWidgets()
        self.xlabelW.setText(self.xtitle)
        self.ylabelW.setText(self.ytitle)
        self.appendDataWidgets()

    def appendDataWidgets(self):
        self.i = 12
        for name in self.names : 

            self.labels[name] = QLabel(name + " ", self)        
            self.labels[name].setAlignment(Qt.AlignRight | Qt.AlignVCenter)

            self.styles[name] = LineStylePicker(self.node, name, self.linestyle[name])

            self.labels[name].setStyleSheet("margin-top: 15px;")
            self.styles[name].setStyleSheet("margin-top: 15px;")

            self.layout.addWidget(self.labels[name], self.i, 0)
            self.layout.addWidget(self.styles[name], self.i, 1)
            self.i += 1


            self.sizes[name]  = LineSizePicker(self.node, name, self.linesize[name])
            self.sizes[name].setAlignment(Qt.AlignLeft | Qt.AlignVCenter)

            self.colors[name] = ColorPicker(self.node, name, color=self.linecolor[name])


            self.layout.addWidget(self.sizes[name],  self.i, 0)
            self.layout.addWidget(self.colors[name], self.i, 1)
            self.i += 1

    def resetWidgets(self):
        self.resetProperties()
        self.names     = {}
        self.linesize  = {}
        self.linecolor = {}
        self.linestyle = {}
        self.graphtype  = {}
        self.c = 0

    def cleanProperties(self):
        for input in self.node.inputs:
            if not input.value : continue

            value  = input.value
            x_name = list(value.keys())[0]

            for name in value:
                if name != x_name:
                    if name not in self.names : 
                        self.names[name]     = name
                        self.linecolor[name] = COLORS[self.c]
                        self.linestyle[name] = "solid"
                        self.graphtype[name] = "line"
                        self.linesize[name]  = 2.0
                        self.c += 1

        for name in self.names:
            if name != x_name:
                if name in self.labels:
                    self.layout.removeWidget(self.labels[name])
                    self.layout.removeWidget(self.styles[name])
                    self.layout.removeWidget(self.sizes[name])
                    self.layout.removeWidget(self.colors[name])

                    self.labels[name].setParent(None)
                    self.styles[name].setParent(None)
                    self.sizes[name].setParent(None)
                    self.colors[name].setParent(None)
                    
                    del self.labels[name]
                    del self.styles[name]
                    del self.sizes[name]
                    del self.colors[name]


        remove = []
        for name in reversed(self.names):
            to_remove = True
            for input in self.node.inputs:
                if name in input.value: 
                    to_remove = False
            if to_remove: remove.append(name)

        for name in remove:
            del self.names[name]
            del self.linecolor[name]
            del self.linestyle[name]
            del self.graphtype[name]
            del self.linesize[name]


    def serialize(self):
        res = super().serialize()
        res['xtitle']     = self.xtitle
        res['ytitle']     = self.ytitle
        """
        styles  = []
        colors  = []
        types   = []
        names   = []
        sizes   = []

        for name in self.names:
            styles.append(self.linestyle[name])
            colors.append(self.linecolor[name])
            types.append( self.graphtype[name])
            sizes.append( self.linesize[name])
            names.append(name)

        res['styles'] = styles
        res['colors'] = colors
        res['types']  = types
        res['sizes']  = sizes
        res['names']  = names
        """
        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                #self.resetWidgets()
                if 'xtitle'      in data : self.xtitle = data['xtitle']
                if 'ytitle'      in data : self.ytitle = data['ytitle']
                """
                for i, name in enumerate(data['names']):
                    self.names[name]     = name
                    self.linecolor[name] = data['colors'][i]
                    self.linestyle[name] = data['styles'][i]
                    self.graphtype[name] = data['types'][i]
                    self.linesize[name]  = data['sizes'][i]

                    self.c += 1
                """
                self.fillWidgets()
            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res



@register_node(OP_MODE_PLOT)
class GraphicsOutputNode(ResizableInputNode):
    icon = "icons/valoutput.png"
    op_code = OP_MODE_PLOT
    op_title = "Plot"

    def __init__(self, scene, inputs=[1], outputs=[]):
        super().__init__(scene, inputs, outputs)
        self.insockets    = []
        self.linecolor = {}
        self.linestyle = {}
        self.graphtype = {}
        self.linesize  = {}
        self.c = 0
        self.input_socket_position  = LEFT_TOP

    def onInputChange(self, new_edge=None):
        self.content.changed.emit()

    def resize(self):
        pass

    def initInnerClasses(self):
        self.content    = GraphicsOutputContent(self)
        self.grNode     = GraphicsOutputGraphicsNode(self)
        self.properties = GraphicsProperties(self)
        self.content.changed.connect(self.recalculateNode)


    def prepareSettings(self):
        #self.properties.resetProperties()
        self.properties.cleanProperties()
        self.properties.fillWidgets()
        return True

    def prepareCanvas(self):
        if not self.hasValue(0) : return

        value = self.getInput(0).value
        self.content.axes.clear()
        x_name = list(value.keys())[0]
        self.x_val  = value[x_name]

        if self.properties.xtitle == "":
            self.content.axes.set_xlabel(x_name)
        else:
            self.content.axes.set_xlabel(self.properties.xtitle)

        self.content.axes.set_ylabel(self.properties.ytitle)
        self.content.axes.xaxis.label.set_size( self.properties.labelsize )
        self.content.axes.yaxis.label.set_size( self.properties.labelsize )

        self.content.axes.tick_params(labelsize= self.properties.ticksize)
        self.content.axes.tick_params(labelsize= self.properties.ticksize)

    def addData(self, value):
        if not value : return 

        x_name = list(value.keys())[0]
        x_val  = value[x_name]
        type = 'line'

        for i, name in enumerate(value):
            if name != x_name:
                if self.properties.graphtype[name] == 'line':
                    self.content.axes.plot(x_val, value[name], label=name, 
                                            color=self.properties.linecolor[name], linestyle=self.properties.linestyle[name],
                                            linewidth=self.properties.linesize[name])

                if self.properties.graphtype[name] == 'scatter':
                    self.content.axes.scatter(x_val, value[name], label=name, 
                                            color=self.properties.linecolor[name], marker=self.properties.linestyle[name],
                                            s=self.properties.linesize[name]*10)

    def drawPlot(self):
        self.content.axes.clear()
        self.prepareCanvas()
        for input in self.insockets:
            self.addData(input.value)
        self.content.axes.legend(loc = 1, fontsize=self.properties.legendsize)
        self.content.canvas.draw()


    def evalImplementation(self, silent=False):
        self.insockets = self.getInputs()
        if not self.insockets:
            self.setInvalid()
            self.e = "Does not have and intry Node"
            return False
        else:
            self.sortSockets()
            if len(self.insockets) > 0:
                self.setDirty(False)
                self.setInvalid(False)
                self.e = ""
                self.value = {}

                self.drawPlot()

                return True
            else:
                self.setDirty(False)
                self.setInvalid(False)
                self.e = "Not enough input data"
                self.getOutput(0).value = 0
                self.getOutput(0).type = "float"
                return False










class TernaryPlotCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig  = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.fig.add_axes([0.1, 0.1,  0.75, 0.85])
        self.bar  = self.fig.add_axes([0.9, 0.1,  0.05, 0.85])
        super(TernaryPlotCanvas, self).__init__(self.fig)

class TernaryPlotGraphicsNode(ResizebleDataNode):
    def initSizes(self):
        super().initSizes()
        self.width  = 300.0
        self.height = 300.0

class TernaryPlotContent(DataContent):
    def initUI(self):
        super().initUI()
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        self.canvas = TernaryPlotCanvas()
        self.layout.addWidget(self.canvas)


    def serialize(self):
        res = super().serialize()
        res['width'] = self.node.grNode.width
        res['height'] = self.node.grNode.height
        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                self.node.grNode.height = data['height']
                self.node.grNode.width  = data['width']
                self.updateSize()
            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res

class TernaryPlotProperties(PlotProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)

        self.levels     = 10
        self.barfont    = 12
        self.xtitle     = ""
        self.ytitle     = ""
        self.ztitle     = ""

        label = QLabel("levels ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.levelsW  = QLineEdit(str(self.levels), self)
        self.levelsW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 9, 0)
        self.layout.addWidget(self.levelsW, 9, 1)
        label.setStyleSheet("margin-bottom: 5px;")
        self.levelsW.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("bar font size ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.barfontW  = QLineEdit(str(self.barfont), self)
        self.barfontW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 10, 0)
        self.layout.addWidget(self.barfontW, 10, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.barfontW.setStyleSheet("margin-bottom: 15px;")


        label = QLabel("Axis names ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 11, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("x name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.xlabelW  = QLineEdit(self.xtitle, self)
        self.xlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 12, 0)
        self.layout.addWidget(self.xlabelW, 12, 1)

        label = QLabel("y name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ylabelW  = QLineEdit(self.ytitle, self)
        self.ylabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 13, 0)
        self.layout.addWidget(self.ylabelW, 13, 1)

        label = QLabel("z name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.zlabelW  = QLineEdit(self.ztitle, self)
        self.zlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 14, 0)
        self.layout.addWidget(self.zlabelW, 14, 1)

        label.setStyleSheet("margin-bottom: 15px;")
        self.zlabelW.setStyleSheet("margin-bottom: 15px;")

        self.levelsW.returnPressed.connect(self.updateData)
        self.barfontW.returnPressed.connect(self.updateData)
        self.xlabelW.returnPressed.connect(self.updateData)
        self.ylabelW.returnPressed.connect(self.updateData)
        self.zlabelW.returnPressed.connect(self.updateData)


    def updateContent(self):
        super().updateContent()
        self.levels       = int(self.levelsW.text())
        self.barfont      = float(self.barfontW.text())
        self.xtitle       = self.xlabelW.text()
        self.ytitle       = self.ylabelW.text()
        self.ztitle       = self.zlabelW.text()

    def fillWidgets(self):
        super().fillWidgets()
        self.levelsW.setText(str(self.levels))
        self.xlabelW.setText(self.xtitle)
        self.ylabelW.setText(self.ytitle)
        self.zlabelW.setText(self.ztitle)
        self.barfontW.setText(str(self.barfont))

    def serialize(self):
        res = super().serialize()
        res['barfont']    = self.barfont
        res['levels']     = self.levels
        res['xtitle']     = self.xtitle
        res['ytitle']     = self.ytitle
        res['ztitle']     = self.ztitle

        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                if 'levels'      in data : self.levels  = data['levels']
                if 'xtitle'      in data : self.xtitle  = data['xtitle']
                if 'ytitle'      in data : self.ytitle  = data['ytitle']
                if 'ztitle'      in data : self.ztitle  = data['ztitle']
                if 'barfont'     in data : self.barfont = data['barfont']

                self.fillWidgets()

            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res


@register_node(OP_MODE_PLOT_TERNARY)
class TernaryPlotNode(ResizableInputNode):
    icon = "icons/valoutput.png"
    op_code = OP_MODE_PLOT_TERNARY
    op_title = "Ternary Diagram"

    def __init__(self, scene, inputs=[1], outputs=[]):
        super().__init__(scene, inputs, outputs)
        self.vmax = 1.0
        self.vmin = 0.0
        self.cmap = plt.cm.viridis
        self.insockets = []

    def initInnerClasses(self):
        self.content = TernaryPlotContent(self)
        self.grNode  = TernaryPlotGraphicsNode(self)
        self.properties = TernaryPlotProperties(self)
        self.content.changed.connect(self.recalculateNode)

    def prepareSettings(self):
        return True

    def resize(self):
        pass

    def cAxes(self, ax, x, y, pos='x'):
        xmin = x[0]
        xmax = x[-1]
        ymin = y[0]
        ymax = y[-1]
        ax.plot(x, y, color='black')
        
        xt = xmin
        yt = ymin
        dx = (xmax - xmin) / 10
        dy = (ymax - ymin) / 10
        x2 = ( dx * np.cos(1.5708) - dy * np.sin(1.5708) ) / 5 
        y2 = ( dy * np.cos(1.5708) + dx * np.sin(1.5708) ) / 5 
        for i in range(0, 11):
            ax.plot([xt, xt+x2], [yt, yt+y2], color='black')
            ax.text(xt + 1.75*x2, yt + 1.75*y2, i/10,
                    horizontalalignment='center',
                    verticalalignment='center',
                    color='black', fontsize=self.properties.ticksize)
            xt = xt + dx
            yt = yt + dy
        
        
        
    def mesh(self, num):
        # creating mesh 
        sc = 0.5 * np.sqrt(3) * 1.0
        dx = 1.0 / (num)
        dy = 1.0 / (num)
        size = int((num+1) * ((num+1) + 1) / 2)
        xm = np.zeros(size)
        ym = np.zeros(size)
        index = 0
        for i in range(0, (num+1)):
            for n in range(0, i+1):
                xm[index] = 0.5 - 0.5 * i / num + 1.0 * n / num
                ym[index] = (1.0 - dy * i) * sc
                index = index + 1
        
        return np.array([xm, ym]).transpose()
    

    def drawTernary(self, value):
        self.content.canvas.axes.clear()
        if not value : return
        names = list(value.keys())

        # barycentric coords: (a,b,c)
        self.a = np.array( value[names[0]] )
        self.b = np.array( value[names[1]] )
        self.c = np.array( value[names[2]] )
    
        # values is stored in the last column
        self.v = np.array( value[names[-1]] )
        self.vmin = self.v.min()
        self.vmax = self.v.max()

        # translate the data to cartesian corrds
        self.x = 0.5 * ( 2. * self.b + self.c ) / ( self.a + self.b + self.c )
        self.y = 0.5 * np.sqrt(3) * self.c / (self.a + self.b + self.c)
        ylim   = 0.5 * np.sqrt(3) * 1.0
            
        #add frame
        self.cAxes(self.content.canvas.axes, [0, 0.5], [0, ylim])
        self.cAxes(self.content.canvas.axes, [1, 0],   [0, 0])
        self.cAxes(self.content.canvas.axes, [0.5, 1.0], [ylim, 0])
        
        # plot the contour
        im = self.content.canvas.axes.tricontourf(self.x, self.y, self.v, 
                                                  cmap=self.cmap, 
                                                  vmin=self.vmin, vmax=self.vmax,
                                                  levels=self.properties.levels)
        self.content.canvas.bar.set
        self.content.canvas.bar.set_title(names[-1], fontsize = self.properties.barfont)
        self.content.canvas.bar.tick_params(labelsize = self.properties.barfont)
        plt.colorbar(im, cax=self.content.canvas.bar)
        
        
        #position the axis labels
        self.content.canvas.axes.text(1.05, -0.05,  self.properties.ytitle, fontsize=self.properties.labelsize, ha="left")
        self.content.canvas.axes.text(0.50,  0.92,  self.properties.ztitle, fontsize=self.properties.labelsize, ha="center")
        self.content.canvas.axes.text(-0.05,-0.05,  self.properties.xtitle, fontsize=self.properties.labelsize, ha="right")

    def addScatter(self, value):
        names = list(value.keys())

        # barycentric coords: (a,b,c)
        self.a = np.array( value[names[0]] )
        self.b = np.array( value[names[1]] )
        self.c = np.array( value[names[2]] )
    
        # values is stored in the last column
        self.v = np.array( value[names[-1]] )

        # translate the data to cartesian corrds
        self.x = 0.5 * ( 2. * self.b + self.c ) / ( self.a + self.b + self.c )
        self.y = 0.5 * np.sqrt(3) * self.c / (self.a + self.b + self.c)
        ylim   = 0.5 * np.sqrt(3) * 1.0
                    
        # plot the contour
        self.content.canvas.axes.scatter(self.x, self.y, s=200, c=self.v, 
                                         cmap=self.cmap, vmin=self.vmin, vmax=self.vmax, 
                                         edgecolors='black', linewidths=1.0)

    def drawPlot(self):
        self.drawTernary(self.insockets[0].value)
        if len(self.insockets) > 1:
            for input in self.insockets[1:]:
                if input.value:
                    self.addScatter(input.value)

        self.content.canvas.axes.axes.tick_params(labelsize= self.properties.ticksize)

        sc_w = (1.0 - self.properties.marginRight  * 0.01 - self.properties.marginLeft * 0.01)
        sc_h = (1.0 - self.properties.marginBottom * 0.01 - self.properties.marginTop  * 0.01)

        self.content.canvas.bar.set_position([
                            self.properties.marginLeft * 0.01 + sc_w * 0.95, 
                            self.properties.marginBottom * 0.01 + 0.10, 
                            sc_w * 0.05,
                            sc_h * 0.85])

        self.content.canvas.axes.set_position([
                            self.properties.marginLeft * 0.01, 
                            self.properties.marginBottom * 0.01, 
                            sc_w * 0.9, 
                            sc_h])
                    
        self.content.canvas.axes.set_axis_off()
        self.content.canvas.draw()


    def evalImplementation(self, silent=False):
        self.insockets = self.getInputs()
        if not self.insockets:
            if DEBUG : print("OUTNODE_TXT: no input edge")
            self.setInvalid()
            if DEBUG : print("OUTNODE_TXT: set invalid")
            self.e = "Does not have and intry Node"
            if DEBUG : print("OUTNODE_TXT: clear the content")
            return False
        else:            
            if DEBUG : print("OUTNODE_TXT: process the input edge data")
            self.setDirty(False)
            self.setInvalid(False)
            if DEBUG : print("OUTNODE_TXT: reset Dirty and Invalid")
            self.e = ""
            self.value = self.insockets[0].value
            self.drawPlot()

        return True












class HeatMapCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.fig  = Figure(figsize=(width, height), dpi=dpi, tight_layout=True)
        self.axes = self.fig.add_axes([0.1,  0.1, 0.75, 0.85])
        self.bar  = self.fig.add_axes([0.90, 0.1, 0.05, 0.75])

        super(HeatMapCanvas, self).__init__(self.fig)

class HeatMapGraphicsNode(ResizebleDataNode):
    def initSizes(self):
        super().initSizes()
        self.width  = 300.0
        self.height = 300.0

class HeatMapContent(DataContent):
    def initUI(self):
        super().initUI()
        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        self.canvas = HeatMapCanvas()
        self.layout.addWidget(self.canvas)


    def serialize(self):
        res = super().serialize()
        res['width']  = self.node.grNode.width
        res['height'] = self.node.grNode.height
        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                self.node.grNode.height = data['height']
                self.node.grNode.width  = data['width']
                self.updateSize()
            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res

class HeatMapProperties(PlotProperties):
    def __init__(self, node, parent=None):
        super().__init__(node)

        self.levels     = 10
        self.xtitle     = ""
        self.ytitle     = ""

        label = QLabel("levels ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.levelsW  = QLineEdit(str(self.levels), self)
        self.levelsW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 9, 0)
        self.layout.addWidget(self.levelsW, 9, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.levelsW.setStyleSheet("margin-bottom: 15px;")

        label = QLabel("Axis names ", self)        
        label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 10, 0, 1, 2)
        label.setStyleSheet("margin-bottom: 5px;")

        label = QLabel("x name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.xlabelW  = QLineEdit(self.xtitle, self)
        self.xlabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 11, 0)
        self.layout.addWidget(self.xlabelW, 11, 1)

        label = QLabel("y name ", self)        
        label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.ylabelW  = QLineEdit(self.ytitle, self)
        self.ylabelW.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
        self.layout.addWidget(label, 12, 0)
        self.layout.addWidget(self.ylabelW, 12, 1)
        label.setStyleSheet("margin-bottom: 15px;")
        self.ylabelW.setStyleSheet("margin-bottom: 15px;")

        self.levelsW.returnPressed.connect(self.updateData)
        self.xlabelW.returnPressed.connect(self.updateData)
        self.ylabelW.returnPressed.connect(self.updateData)


    def updateContent(self):
        super().updateContent()
        self.levels       = int(self.levelsW.text())
        self.xtitle       = self.xlabelW.text()
        self.ytitle       = self.ylabelW.text()

    def fillWidgets(self):
        super().fillWidgets()
        self.levelsW.setText(str(self.levels))
        self.xlabelW.setText(self.xtitle)
        self.ylabelW.setText(self.ytitle)

    def serialize(self):
        res = super().serialize()

        res['levels']     = self.levels
        res['xtitle']     = self.xtitle
        res['ytitle']     = self.ytitle

        return res

    def deserialize(self, data, hashmap=[]):
        res = super().deserialize(data, hashmap)
        try:
            try:
                if 'levels'      in data : self.levels = data['levels']
                if 'xtitle'      in data : self.xtitle = data['xtitle']
                if 'ytitle'      in data : self.ytitle = data['ytitle']

                self.fillWidgets()

            except Exception as e: 
                dumpException(e)
            return True & res
        except Exception as e : dumpException(e)
        return res



@register_node(OP_MODE_PLOT_HEATMAP)
class HeatMapNode(ResizableInputNode):
    icon = "icons/valoutput.png"
    op_code = OP_MODE_PLOT_HEATMAP
    op_title = "Heat Map"

    def __init__(self, scene, inputs=[1], outputs=[]):
        super().__init__(scene, inputs, outputs)
        self.vmax = 1.0
        self.vmin = 0.0
        self.cmap = plt.cm.viridis
        self.insockets = []

    def initInnerClasses(self):
        self.content = HeatMapContent(self)
        self.grNode  = HeatMapGraphicsNode(self)
        self.properties = HeatMapProperties(self)
        self.content.changed.connect(self.recalculateNode)

    def prepareSettings(self):
        """
        self.properties.resetProperties()
        self.properties.cleanProperties()
        self.properties.fillWidgets()
        """
        return True

    def prepareSettings(self):
        return True

    def resize(self):
        pass

    def addMap(self, value):
        self.content.canvas.axes.clear()
        if not value : return
        names = list(value.keys())

        # barycentric coords: (a,b,c)
        self.x = np.array( value[names[0]] )
        self.y = np.array( value[names[1]] )
    
        # values is stored in the last column
        self.z = np.array( value[names[-1]] )
        self.vmin = self.z.min()
        self.vmax = self.z.max()

        # plot the contour
        im = self.content.canvas.axes.tricontourf(self.x, self.y, self.z, 
                                                  cmap=self.cmap, vmin=self.vmin, vmax=self.vmax,
                                                  levels=self.properties.levels)
        self.content.canvas.bar.set_title(names[-1])
        plt.colorbar(im, cax=self.content.canvas.bar)
        self.content.canvas.axes.set_xlabel(self.properties.xtitle, fontsize=self.properties.labelsize)
        self.content.canvas.axes.set_ylabel(self.properties.ytitle, fontsize=self.properties.labelsize)
        
        self.content.canvas.axes.axes.tick_params(labelsize= self.properties.ticksize)
        self.content.canvas.axes.axes.tick_params(labelsize= self.properties.ticksize)

        sc_w = (1.0 - self.properties.marginRight  * 0.01 - self.properties.marginLeft * 0.01)
        sc_h = (1.0 - self.properties.marginBottom * 0.01 - self.properties.marginTop  * 0.01)

        self.content.canvas.axes.set_position([
                            self.properties.marginLeft * 0.01, 
                            self.properties.marginBottom * 0.01, 
                            sc_w * 0.9, 
                            sc_h])

        self.content.canvas.bar.set_position([
                            self.properties.marginLeft * 0.01 + sc_w * 0.95, 
                            self.properties.marginBottom * 0.01, 
                            sc_w * 0.05,
                            sc_h * 0.90])

        """
        self.axes = self.fig.add_axes([0.1,  0.1, 0.75, 0.85])
        self.bar  = self.fig.add_axes([0.90, 0.1, 0.05, 0.75])

        self.content.canvas.axes.text(1.05, -0.05,  names[1], fontsize=24, ha="left")
        self.content.canvas.axes.text(0.50,  0.92,  names[2], fontsize=24, ha="center")
        self.content.canvas.axes.text(-0.05,-0.05,  names[0], fontsize=24, ha="right")
        """
        

    def drawPlot(self):
        self.insockets = self.getInputs()

        for input in self.insockets:
            if input.value:
                self.addMap(input.value)
        self.content.canvas.draw()


    def evalImplementation(self, silent=False):
        self.insockets = self.getInputs()
        try:
            if not self.insockets:
                if DEBUG : print("OUTNODE_TXT: no input edge")
                self.setInvalid()
                if DEBUG : print("OUTNODE_TXT: set invalid")
                self.e = "Does not have and intry Node"
                if DEBUG : print("OUTNODE_TXT: clear the content")
                return False
            else:            
                if DEBUG : print("OUTNODE_TXT: process the input edge data")
                self.setDirty(False)
                self.setInvalid(False)
                if DEBUG : print("OUTNODE_TXT: reset Dirty and Invalid")
                self.e = ""
                self.drawPlot()

            return True
        except Exception as e:
            self.setDirty(False)
            self.setInvalid(False)
            self.e = e
            return False

# graph area styles  = []         colors  = []         types   = []         names   = []         sizes   = []          for name in self.names:             styles.append(self.linestyle[name])             colors.append(self.linecolor[name])             types.append( self.graphtype[name])             sizes.append( self.linesize[name])             names.append(name)          res['styles'] = styles         res['colors'] = colors         res['types']  = types         res['sizes']  = sizes         res['names']  = names #self.resetWidgets() for i, name in enumerate(data['names']):                     self.names[name]     = name                     self.linecolor[name] = data['colors'][i]                     self.linestyle[name] = data['styles'][i]                     self.graphtype[name] = data['types'][i]                     self.linesize[name]  = data['sizes'][i]                      self.c += 1 #self.properties.resetProperties() # creating mesh # barycentric coords: (a,b,c) # values is stored in the last column # translate the data to cartesian corrds #add frame # plot the contour #position the axis labels # barycentric coords: (a,b,c) # values is stored in the last column # translate the data to cartesian corrds # plot the contour self.properties.resetProperties()         self.properties.cleanProperties()         self.properties.fillWidgets() # barycentric coords: (a,b,c) # values is stored in the last column # plot the contour self.axes = self.fig.add_axes([0.1,  0.1, 0.75, 0.85])         self.bar  = self.fig.add_axes([0.90, 0.1, 0.05, 0.75])          self.content.canvas.axes.text(1.05, -0.05,  names[1], fontsize=24, ha="left")         self.content.canvas.axes.text(0.50,  0.92,  names[2], fontsize=24, ha="center")         self.content.canvas.axes.text(-0.05,-0.05,  names[0], fontsize=24, ha="right")