Skip to content
Snippets Groups Projects
data_plot.py 17.5 KiB
Newer Older
#!/usr/bin/env python3
import sys
from PyQt5 import QtGui, QtCore, QtWidgets

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from matplotlib import pyplot as pp

import numpy as np
import copy as cp
from runpy import run_path
import pprint
import os
import textwrap
from shutil import copyfile
import warnings
import matplotlib.cbook
warnings.filterwarnings('ignore',category=matplotlib.cbook.mplDeprecation)

pp = pprint.PrettyPrinter(indent=4)
    sendInfo = QtCore.pyqtSignal(object)
    def __init__(self, ownData = None, parent=None):
        super(Window, self).__init__(parent)

        # a figure instance to plot on
        self.figure = Figure()
        #self.ax = self.figure.add_subplot(111)
        self.ax = self.figure.add_axes([0.15,0.25,0.6,0.7])   # left,bottom edge, width, height
        self.setWindowTitle("Plot")
        self.canvas = FigureCanvas(self.figure)

        self.toolbar = NavigationToolbar(self.canvas, self)

        self.button2 = QtWidgets.QCheckBox('Switch axes')
        self.button2.clicked.connect(self.change_sw)
        self.maxBox = QtWidgets.QCheckBox('Show maximum')
        self.maxBox.clicked.connect(self.plot)

        self.button3 = QtWidgets.QPushButton('Apply multiplier')
        self.button3.clicked.connect(self.plot)
        self.button4 = QtWidgets.QPushButton('Generate LaTeX code')
        self.button4.clicked.connect(self.getTeX)
        self.addButton = QtWidgets.QPushButton('Add to LaTeX report')
        self.addButton.clicked.connect(self.emitTeXInfo)
        self.typeButton = QtWidgets.QPushButton('Scatter plot')
        self.plotType = 0
        self.typeButton.clicked.connect(self.changeType)
        #self.button4 = QtWidgets.QPushButton('Clear canvas')
        #self.button4.clicked.connect(self.clearCanvas)

        self.bf = QtGui.QFont("Arial",13,QtGui.QFont.Bold)


        self.mult = QtWidgets.QLineEdit("1")
        self.mult.setAlignment(QtCore.Qt.AlignCenter)
        self.mult.setValidator(QtGui.QDoubleValidator())
        # data loading, we can have more than one y_data! TODO
        if not ownData:
            self.d = run_path("get_data_for_gui.py")
        else:
            self.d = ownData


        self.combo_reg = QtWidgets.QComboBox(self)
        self.combo_reg.addItem('Overall summary')
        #TODO - multiple measurements handling (2 in range is ad hoc)
        if "nested_regions_report_data" in self.d.keys():
            for i in range(0,len(self.d["nested_regions_report_data"]),2):
                comboItem = "REGION - {}".format(self.d["nested_regions_report_data"][i]["nested_region"])
                if comboItem not in [self.combo_reg.itemText(i) for i in range(self.combo_reg.count())]:
                    self.combo_reg.addItem(comboItem)

        if len(self.data) > 1:
            self.combo = QtWidgets.QComboBox(self)
            for i in range(0,len(self.data)):
                self.combo.addItem("{}, {}".format(self.data[i][0]["category"],self.data[i][0]["arg"]))
        # set the layout
        layout = QtWidgets.QVBoxLayout()
        self.keyList = list(self.plot_data[1]["key"])
        for i in range(0,len(self.keyList)):
            self.keyList[i] = self.keyList[i].replace(';',' ')
        self.lab_reg = QtWidgets.QLabel('Choose Area: ')
        self.lab_reg.setFont(self.bf)
        hl_reg = QtWidgets.QHBoxLayout()
        hl_reg.addWidget(self.lab_reg)
        hl_reg.addWidget(self.combo_reg)
        self.combo_reg.activated[str].connect(self.chooseReg)
        layout.addLayout(hl_reg)

        if len(self.data) > 1:
            self.label = QtWidgets.QLabel('Choose quantity: ')
            self.label.setFont(self.bf)
            hl = QtWidgets.QHBoxLayout()
            hl.addWidget(self.label)
            hl.addWidget(self.combo)
            self.combo.activated[str].connect(self.chooseData)
            layout.addLayout(hl)


        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)

        hlayout = QtWidgets.QHBoxLayout()
        hlayout.addWidget(self.mult)
        hlayout.addWidget(self.button3)
        #hlayout2 = QtWidgets.QHBoxLayout()
        hlayout.addWidget(self.button2)
        hlayout.addWidget(self.maxBox)
        hlayout3 = QtWidgets.QHBoxLayout()
        hlayout3.addWidget(self.typeButton)
        hlayout3.addWidget(self.button4)
        hlayout3.addWidget(self.addButton)

        layout.addLayout(hlayout3)
        self.resize(800,800)


    def changeType(self):
        self.plotType = not self.plotType
        if self.plotType:
            self.typeButton.setText('Line plot')
        else:
            self.typeButton.setText('Scatter plot')
    def emitTeXInfo(self):
        if str(self.combo_reg.currentText()) == "Overall summary":
            reg = list(self.d['config']['main_reg'][0].keys())[0]
        else:
            reg = str(self.combo_reg.currentText())[9:]
        for i in range(len(self.data)):
            if self.data[i][0]['arg'] in str(self.combo.currentText()):
                q = self.data[i][0]['arg']
                c = self.data[i][0]['category']
        self.sendInfo.emit({'quantity': q, 'category': c, 'region': reg, 'multiplier': float(self.mult.text()), 'switched': self.sw, 'dot': self.plotType})
        self.addButton.setEnabled(False)
        msg = QtWidgets.QMessageBox()
        msg.setIcon(QtWidgets.QMessageBox.Information)
        msg.setText("Current plot was added to LaTeX report.")
        msg.setWindowTitle("LaTeX report info")
        msg.setStandardButtons(QtWidgets.QMessageBox.Close)
        msg.buttonClicked.connect(msg.close)
        msg.exec_()

    def getTeX(self):
        dlg = QtWidgets.QFileDialog()
        save_file_path = str(dlg.getSaveFileName(filter='LaTeX code (*.tex)')[0])
        if save_file_path and not save_file_path.endswith('.tex'):
            save_file_path = save_file_path + '.tex' 

        #save_file_path = "/home/david/SGS18-READEX/graf.tex"
        target_file = '/'.join(save_file_path.split('/')[0:-1])+'/readex_header.tex'
        copyfile(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))+'/input/readex_header.tex',target_file)

        tex_file = open(save_file_path,"w")
        tex_file.write(r'\documentclass{article}')
        tex_file.write('''\n\input{readex_header}\n''')
        tex_file.write(r'\begin{document}')

        title = '{}, {}'.format(str(self.combo_reg.currentText()),str(self.combo.currentText()))
        x_lab = "{}\,[{}{}]".format(self.plot_data[1]["x_label_name"],'' if self.numMultiplier.is_integer() and int(self.numMultiplier) == 1 else 1/self.numMultiplier,
        y_lab = "{} [{}]".format(self.plot_data[0]["arg"],self.plot_data[0]["unit"])
        func_lab = "{}\,[{}]".format(self.plot_data[1]["func_label_name"],self.plot_data[1]["func_label_unit"])

        header_code = (r'''
                \begin{{adjustbox}}{{center, valign=t}}
                \begin{{tikzpicture}}
                \begin{{axis}}[
                align=center,
                title={{ {} }},
                xlabel={{ {} }},
                ylabel={{ {} }},
                legend pos=outer north east,
                xmajorgrids=true,
                ymajorgrids=true,
                grid style=dashed,
                width=\textwidth*0.9,
                height=\textwidth*0.6,
                cycle list name = color list,
                each nth point = 1,
                filter discard warning=false
                ]
                \addlegendimage{{empty legend}}
                '''.format(title, y_lab if self.sw else x_lab, x_lab if self.sw else y_lab))

        tex_file.write(textwrap.dedent(header_code))
        tex_file.write('\n\n')

        for i in range(0,self.n):
            code = (r'''\addplot+ [mark=triangle*{}] coordinates {{ {}
                     }};
                     '''.format(',only marks' if only_marks else '', '\n'.join([str((self.numMultiplier*e[a],e[b])) for e in self.K[i]])))
            tex_file.write(code)

        legend_title = r'\hspace{{-.6cm}} {} '.format("{} [{}]".format(self.plot_data[1]["func_label_name"],self.plot_data[1]["func_label_unit"]))

        ret = ''
        if not self.k:
            ret = '\legend{{ {} }}'.format(','.join(filter(None, [legend_title] + [str(i) for i in range(self.n)])))
            ret = '\legend{{ {} }}'.format(','.join(filter(None, [legend_title] + [str(item) for item in self.k])))

        tex_file.write(ret)

        optim_code = r'''
                \addplot [only marks] table {{
                    {0} {1}
                }} node [pin={{[pin distance={4}pt]{3}:{{\footnotesize( {2} )}}}}]{{}};
                '''
        optim_code = textwrap.dedent(optim_code)
        title_angle = title_angle = 210 if self.optx > 0.5*(min(self.ky)+max(self.ky)) else 330
        title_distance = 2
        optim_title = 'optimal settings are {}: {} {}, {}: {} {}'.format(self.plot_data[1]["x_label_name"],self.optx,self.plot_data[1]["x_label_unit"],
                                                                    self.plot_data[1]["func_label_name"],self.optf,self.plot_data[1]["func_label_unit"])
        optim_code = optim_code.format(self.ymin if self.sw else self.numMultiplier*self.optx, self.numMultiplier*self.optx if self.sw else self.ymin,
                           optim_title,
                           title_angle, title_distance)

        tex_file.write(optim_code)
        tex_file.write('\n\end{axis}\n\end{tikzpicture}\n\end{adjustbox}\n\n\end{document}')

        tex_file.close()

    def chooseReg(self, dataLabel):
        if dataLabel == "Overall summary":
            self.data = self.d["plot_summary_data"]
        else:
            for i in range(0,len(self.d["nested_regions_report_data"])):
                tmp = "REGION - {}".format(self.d["nested_regions_report_data"][i]["nested_region"])
                if dataLabel == tmp:
                    self.data = self.d["nested_regions_report_data"][i]["plot_data"]
        self.plot_data = self.data[0]
        if len(self.data) > 1:
            for i in range(0,len(self.data)):
                if str(self.combo.currentText()) == "{}, {}".format(self.data[i][0]["category"],self.data[i][0]["arg"]):

        self.keyList = list(self.plot_data[1]['key'])
        for i in range(0,len(self.keyList)):
            self.keyList[i] = self.keyList[i].replace(';',' ')
    def chooseData(self, dataLabel):
        for i in range(0,len(self.data)):
            if dataLabel == "{}, {}".format(self.data[i][0]["category"],self.data[i][0]["arg"]):
        '''p = self.grab()
        print(p)
        p.save('/home/david/SGS18-READEX/plotGUI.png')'''
    def change_sw(self):
        self.sw = not self.sw
        self.ax = self.figure.add_axes([0.15,0.25,0.6,0.7])   # left,bottom edge, width, height
        if self.mult.text() in ["e","."] or "," in self.mult.text() or self.mult.text().endswith('e') or self.mult.text().startswith('e'):
            self.numMultiplier = 1.0
            msg = QtWidgets.QMessageBox()
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText("Wrong multiplier format!\nPlease write the multiplier in form AeB (e.g. 1e-4).")
            msg.setWindowTitle("Invalid multiplier")
            msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msg.exec_()
            self.mult.setText("1")
        elif not float(self.mult.text()):
            self.numMultiplier = 1.0
            msg = QtWidgets.QMessageBox()
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText('Multiplier should be a positive number!\nPlease choose non-zero multiplier.')
            msg.setWindowTitle("Invalid multiplier")
            msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msg.exec_()
            self.mult.setText("1")
        else:
            self.numMultiplier = float(self.mult.text())
        self.k = list(self.plot_data[1]["lines"])
        self.keyStr = ""
        
        for i in range(len(self.keyList)):
            if i == 0:
                self.keyStr = ", key: "
            self.keyStr = self.keyStr + self.keyList[i]
            if i < len(self.keyList)-1:
                self.keyStr = self.keyStr + ", "


        self.K = self.plot_data[1]["heat_data"]
        self.ky = [h[0] for h in self.K[0]]
        self.n = len(self.k)
        self.optx = self.plot_data[1]["optim_x_val"]
        self.optf = self.plot_data[1]["optim_func_label_value"]
        self.ymin = min([y[1] for y in self.K[0]])
        for i in range(0,len(self.K)):
            for j in range(0,len(self.K[i])):
                tmp = list(self.K[i][j])
                self.K[i][j] = tuple(tmp)
        for m in range(0,self.n):
            X = [x[0] for x in self.K[m]]
            Y = [x[1] for x in self.K[m]]

            xmin = min(X)
            xmax = max(X)
            ymin0 = min(Y)
            ymax0 = max(Y)
            self.avx = 0.5*(xmin+xmax)
            if self.ymin > ymin0:
                self.ymin = ymin0
            for idx,it in enumerate(Y):
                if it == ymax0 and it > ymax:
                    ymax = ymax0
                    xmax = X[idx]
                    self.funcmax = self.k[m]
                if self.plotType:
                    self.ax.plot(Y,X,'o',label = self.k[m])
                else:
                    self.ax.plot(Y,X,label = self.k[m])
                if self.plotType:
                    self.ax.plot(X,Y,'o',label = self.k[m])
                else:
                    self.ax.plot(X,Y,label = self.k[m])
        self.xlab = "{} [{}{}]".format(self.plot_data[1]["x_label_name"],'' if self.numMultiplier == 1 else 1/self.numMultiplier,self.plot_data[1]["x_label_unit"])
        self.ylab = "{} [{}]".format(self.plot_data[0]["arg"],self.plot_data[0]["unit"])

        if self.maxBox.isChecked():
            optStr = 'optimal settings are {}: {} {}, {}: {} {}{}\nMaximum value is at settings {}: {} {}, {}: {} {}'.format(self.plot_data[1]["x_label_name"],
                                                                                                        self.optx,self.plot_data[1]["x_label_unit"],
                                                                                                        self.plot_data[1]["func_label_name"],
                                                                                                        self.optf,self.plot_data[1]["func_label_unit"],self.keyStr,
                                                                                                        self.plot_data[1]["x_label_name"],xmax,self.plot_data[1]["x_label_unit"],
                                                                                                        self.plot_data[1]["func_label_name"],
                                                                                                        self.funcmax,self.plot_data[1]["func_label_unit"])
        else:
            optStr = 'optimal settings are {}: {} {}, {}: {} {}{}'.format(self.plot_data[1]["x_label_name"],
                                                                self.optx,self.plot_data[1]["x_label_unit"],
                                                                self.plot_data[1]["func_label_name"],
                                                                self.optf,self.plot_data[1]["func_label_unit"],self.keyStr)

            self.ax.set_ylabel(self.xlab, fontsize = 13)
            self.ax.set_xlabel(self.ylab, fontsize = 13)
            self.ax.text(1.1*self.ymin-0.1*ymax,1.3*xmin-0.3*xmax,optStr)
            self.ax.set_xlabel(self.xlab, fontsize = 13)
            self.ax.set_ylabel(self.ylab, fontsize = 13)
            self.ax.text(1.1*xmin-0.1*xmax,1.3*self.ymin-0.3*ymax,optStr)


        self.ax.tick_params(labelsize = 11)

        self.ax.grid(linestyle = '--')

        handles, labels = self.ax.get_legend_handles_labels()

        lgd = self.ax.legend(handles,labels,loc=2,bbox_to_anchor=(1,1.025),ncol=1, borderaxespad = 0.,prop={'size': 13})
        lgd.set_title("{}\n[{}]".format(self.plot_data[1]["func_label_name"],self.plot_data[1]["func_label_unit"]))
        for i in range(0,len(self.K)):
            for j in range(0,len(self.K[i])):
                tmp = list(self.K[i][j])
                self.K[i][j] = tuple(tmp)
        #self.resize((self.figure.get_size_inches()*self.figure.dpi)[0],self.height())


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    sys.exit(app.exec_())