Source code for daetools.dae_plotter.plotter

#!/usr/bin/env python
"""********************************************************************************
                             plotter.py
                 DAE Tools: pyDAE module, www.daetools.com
                 Copyright (C) Dragan Nikolic
***********************************************************************************
DAE Tools is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License version 3 as published by the Free Software
Foundation. DAE Tools is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with the
DAE Tools software; if not, see <http://www.gnu.org/licenses/>.
********************************************************************************"""
import os, sys, distutils.sysconfig, types, json, numpy, webbrowser
from os.path import join, realpath, dirname
from PyQt5 import QtCore, QtGui, QtWidgets
from daetools.pyDAE import *
from .choose_variable import daeChooseVariable
from .custom_plots import daeCustomPlots
from .about import daeAboutDialog
from .plot2d import dae2DPlot
from .user_data import daeUserData
from .data_receiver_io import pickleProcess, unpickleProcess

python_major = sys.version_info[0]
python_minor = sys.version_info[1]
python_build = sys.version_info[2]

try:
    images_dir = join(dirname(__file__), 'images')
except:
    # In case we are running the module on its own (i.e. as __main__)
    images_dir = join(realpath(dirname(sys.argv[0])), 'images')

[docs]class daeMainWindow(QtWidgets.QMainWindow): def __init__(self, tcpipServer): QtWidgets.QMainWindow.__init__(self) self.tcpipServer = tcpipServer self.loadedProcesses = [] self.move(0, 0) self.resize(400, 200) self.setWindowIcon(QtGui.QIcon(join(images_dir, 'daetools-48x48.png'))) self.setWindowTitle("DAE Tools Plotter v%s [py%d.%d]" % (daeVersion(True), python_major, python_minor)) exit = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'close.png')), 'Exit', self) exit.setShortcut('Ctrl+Q') exit.setStatusTip('Exit application') exit.triggered.connect(self.close) saveProcess = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'filesave.png')), 'Save simulation data...', self) saveProcess.setShortcut('Ctrl+S') saveProcess.setStatusTip('Save simulation data to a file') saveProcess.triggered.connect(self.slotSaveProcess) openProcess = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'fileopen.png')), 'Open simulation data...', self) openProcess.setShortcut('Ctrl+O') openProcess.setStatusTip('Open simulation data from a file') openProcess.triggered.connect(self.slotOpenProcess) plot2D = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'add-2d.png')), 'New 2D plot...', self) plot2D.setShortcut('Ctrl+2') plot2D.setStatusTip('New 2D plot') plot2D.triggered.connect(self.slotPlot2D) autoupdatePlot2D = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'add-autoupdate-2d.png')), 'New auto update 2D plot...', self) autoupdatePlot2D.setShortcut('Ctrl+U') autoupdatePlot2D.setStatusTip('New auto update 2D plot') autoupdatePlot2D.triggered.connect(self.slotPlot2DAutoUpdate) animatedPlot2D = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'add-ani-2d.png')), 'New animated 2D plot...', self) animatedPlot2D.setShortcut('Ctrl+A') animatedPlot2D.setStatusTip('New animated 2D plot') animatedPlot2D.triggered.connect(self.plot2DAnimated) var1VSvar2Plot2D = QtWidgets.QAction('New variable1 vs. variable2 2D plot...', self) var1VSvar2Plot2D.setShortcut('Ctrl+B') var1VSvar2Plot2D.setStatusTip('New variable1 vs. variable2 2D plot') var1VSvar2Plot2D.triggered.connect(self.plot2D_Var1_v_var2) customPlots = QtWidgets.QAction('New user-defined plot...', self) customPlots.setShortcut('Ctrl+C') customPlots.setStatusTip('New user-defined plot') customPlots.triggered.connect(self.slotCustomPlots) fromUserData = QtWidgets.QAction('New plot from the user-provided data...', self) fromUserData.setShortcut('Ctrl+D') fromUserData.setStatusTip('New plot from the user-provided data') fromUserData.triggered.connect(self.slotFromUserData) plot3D = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'add-3d.png')), 'New Mayavi 3D plot...', self) plot3D.setShortcut('Ctrl+3') plot3D.setStatusTip('New Mayavi 3D plot') plot3D.triggered.connect(self.slotPlot3D) #matplotlibSurfacePlot = QtWidgets.QAction('New matplotlib surface plot...', self) #matplotlibSurfacePlot.setShortcut('Ctrl+S') #matplotlibSurfacePlot.setStatusTip('New matplotlib surface plot') #matplotlibSurfacePlot.triggered.connect(self.slotMatplotlibSurfacePlot) plotVTK_2D = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'vtk.png')), 'Open 2D VTK plot from file...', self) plotVTK_2D.setShortcut('Ctrl+V') plotVTK_2D.setStatusTip('Open 2D VTK plot from file') plotVTK_2D.triggered.connect(self.slotOpenVTK_2D) saveVTKasImages_2D = QtWidgets.QAction('Export 2D VTK files to PNG images...', self) saveVTKasImages_2D.setShortcut('Ctrl+P') saveVTKasImages_2D.setStatusTip('Export 2D VTK files to PNG images...') saveVTKasImages_2D.triggered.connect(self.slotSaveVTKFilesAsImages_2D) #animateVTKFiles_2D = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'vtk.png')), 'Animate 2D VTK plots...', self) #animateVTKFiles_2D.setShortcut('Ctrl+6') #animateVTKFiles_2D.setStatusTip('Animate 2D VTK plots...') #animateVTKFiles_2D.triggered.connect(self.slotAnimateVTKFiles_2D) openTemplate = QtWidgets.QAction(QtGui.QIcon(join(images_dir, 'template.png')), 'Open 2D template...', self) openTemplate.setShortcut('Ctrl+T') openTemplate.setStatusTip('Open 2D plot emplate') openTemplate.triggered.connect(self.slotOpenTemplate) about = QtWidgets.QAction('About', self) about.setStatusTip('About') about.triggered.connect(self.slotAbout) docs = QtWidgets.QAction('Documentation', self) docs.setStatusTip('Documentation') docs.triggered.connect(self.slotDocumentation) self.statusBar() menubar = self.menuBar() file = menubar.addMenu('&File') file.addAction(openProcess) file.addAction(saveProcess) file.addSeparator() file.addAction(exit) plot = menubar.addMenu('&Plot') plot.addAction(plot2D) plot.addAction(autoupdatePlot2D) plot.addAction(animatedPlot2D) plot.addAction(var1VSvar2Plot2D) plot.addAction(plot3D) #plot.addAction(matplotlibSurfacePlot) plot.addAction(customPlots) plot.addAction(fromUserData) plot.addSeparator() plot.addAction(openTemplate) plot.addSeparator() plot.addAction(plotVTK_2D) plot.addAction(saveVTKasImages_2D) #plot.addAction(animateVTKFiles_2D) help = menubar.addMenu('&Help') help.addAction(about) help.addAction(docs) self.toolbar = self.addToolBar('Main toolbar') self.toolbar.addAction(plot2D) self.toolbar.addAction(animatedPlot2D) self.toolbar.addAction(plot3D) self.toolbar.addSeparator() self.toolbar.addAction(openTemplate) self.toolbar.addSeparator() self.toolbar.addAction(plotVTK_2D)
[docs] def getProcesses(self): processes = [dataReceiver.Process for dataReceiver in self.tcpipServer.DataReceivers] processes.extend(self.loadedProcesses) processes.sort(key=lambda process: process.Name) return processes
#@QtCore.pyqtSlot()
[docs] def slotOpenProcess(self): filename, ok = QtWidgets.QFileDialog.getOpenFileName(self, "Choose simulation data file", '', "DAE Tools Simulation Files (*.simulation);;All Files (*.*)") if not ok: return try: process = unpickleProcess(str(filename)) self.loadedProcesses.append(process) except Exception as e: QtWidgets.QMessageBox.warning(self, "daePlotter", "Cannot open simulation data file.\nError: " + str(e))
#@QtCore.pyqtSlot()
[docs] def slotSaveProcess(self): processes = {} for process in self.getProcesses(): processes[process.Name] = process if not processes: return process_name, ok = QtWidgets.QInputDialog.getItem(self, "Save simulation data", "Choose the simulation:", processes.keys(), 0, False) if not ok: return filename, ok = QtWidgets.QFileDialog.getSaveFileName(self, "Choose simulation data file", '%s.simulation' % process_name, "DAE Tools Simulation Files (*.simulation);;All Files (*.*)") if not ok: return try: process = processes[str(process_name)] pickleProcess(process, str(filename)) except Exception as e: QtWidgets.QMessageBox.warning(self, "daePlotter", "Cannot open simulation data file.\nError: " + str(e))
#@QtCore.pyqtSlot()
[docs] def slotCustomPlots(self): processes = self.getProcesses() dlg = daeCustomPlots() if dlg.exec_() != QtWidgets.QDialog.Accepted: return exec(dlg.plot_source, globals()) if 'make_custom_plot' in globals(): make_custom_plot(processes)
[docs] def slotFromUserData(self): dlg = daeUserData() if dlg.exec_() != QtWidgets.QDialog.Accepted: return plot2D = dae2DPlot(self, 0.0) if plot2D.newCurveFromUserData(dlg.xLabel, dlg.yLabel, dlg.lineLabel, dlg.xPoints, dlg.yPoints) == False: plot2D.close() del plot2D return plot2D.show()
#@QtCore.pyqtSlot()
[docs] def slotPlot2D(self): self.plot2D()
#@QtCore.pyqtSlot()
[docs] def slotPlot2DAutoUpdate(self): msecs, ok = QtWidgets.QInputDialog.getInt(self, 'Insert the update interval', 'Interval (msecs):', 1000, 1, 1E5, 100) if ok: self.plot2D(msecs) # 1000 ms
[docs] def plot2D(self, updateInterval = 0): plot2D = dae2DPlot(self, updateInterval) if plot2D.newCurve() == False: plot2D.close() del plot2D return plot2D.show()
[docs] def plot2DAnimated(self): plot2D = dae2DPlot(self, 100, True) # 100 is some default, just to mark as animated plot if plot2D.newAnimatedCurve() == False: plot2D.close() del plot2D return plot2D.show()
[docs] def plot2D_Var1_v_var2(self): plot2D = dae2DPlot(self, 0) if plot2D.newVar1_vs_Var2Curve() == False: plot2D.close() del plot2D return plot2D.show()
#@QtCore.pyqtSlot()
[docs] def slotPlot3D(self): try: from .mayavi_plot3d import daeMayavi3DPlot except Exception as e: QtWidgets.QMessageBox.warning(self, "daePlotter", "Cannot load mayavi_plot3d module.\nDid you forget to install Mayavi2?\nError: " + str(e)) return plot3D = daeMayavi3DPlot(self) if plot3D.newSurface() == False: del plot3D
""" def slotMatplotlibSurfacePlot(self, updateInterval = 0): try: from .mpl_plot3d import dae3DPlot except Exception as e: QtWidgets.QMessageBox.warning(None, "daePlotter", "Cannot load mpl_plot3d module.\nError: " + str(e)) return plot3D = dae3DPlot(self, updateInterval) if plot3D.newSurface() == False: del plot3D return plot3D.show() """ #@QtCore.pyqtSlot()
[docs] def slotOpenVTK_2D(self): filename, ok = QtWidgets.QFileDialog.getOpenFileName(self, "Choose VTK File", '', "VTK Files (*.vtk)") if not ok: return try: from .mayavi_plot3d import daeMayavi3DPlot except Exception as e: QtWidgets.QMessageBox.warning(self, "daePlotter", "Cannot load 3D Plot module.\nDid you forget to install Mayavi2?\nError: " + str(e)) return daeMayavi3DPlot.showVTKFile_2D(str(filename))
#@QtCore.pyqtSlot()
[docs] def slotSaveVTKFilesAsImages_2D(self): sourceFolder, ok = QtWidgets.QFileDialog.getExistingDirectory(self, "Choose the source folder with VTK files", '', QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if not ok: return destinationFolder, ok = QtWidgets.QFileDialog.getExistingDirectory(self, "Choose the destination folder for generated PNG images", '', QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if not ok: return try: from .mayavi_plot3d import daeMayavi3DPlot except Exception as e: QtWidgets.QMessageBox.warning(self, "daePlotter", "Cannot load 3D Plot module.\nDid you forget to install Mayavi2?\nError: " + str(e)) return daeMayavi3DPlot.saveVTKFilesAsImages_2D(str(sourceFolder), str(destinationFolder))
#@QtCore.pyqtSlot()
[docs] def slotAnimateVTKFiles_2D(self): folder, ok = QtWidgets.QFileDialog.getExistingDirectory(self, "Choose the folder with VTK files", '', QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks) if not ok: return try: from .mayavi_plot3d import daeMayavi3DPlot except Exception as e: QtWidgets.QMessageBox.warning(self, "daePlotter", "Cannot load 3D Plot module.\nDid you forget to install Mayavi2?\nError: " + str(e)) return daeMayavi3DPlot.animateVTKFiles_2D(str(folder))
#@QtCore.pyqtSlot()
[docs] def slotOpenTemplate(self): try: filename, ok = QtWidgets.QFileDialog.getOpenFileName(self, "Open 2D plot template", "", "Templates (*.pt)") if not ok: return f = open(filename, 'r') s = f.read(-1) template = json.loads(s) curves = template['curves'] plotType = int(template['plotType']) updateInterval = float(template['updateInterval']) if plotType == daeChooseVariable.plot2D or plotType == daeChooseVariable.plot2DAutoUpdated: plot2D = dae2DPlot(self, updateInterval, False) elif plotType == daeChooseVariable.plot2DAnimated: plot2D = dae2DPlot(self, updateInterval, True) else: raise RuntimeError('Invalid plot type') if plot2D.newFromTemplate(template) == False: plot2D.close() del plot2D else: plot2D.show() except Exception as e: print((str(e)))
#@QtCore.pyqtSlot()
[docs] def slotAbout(self): dlg = daeAboutDialog() dlg.exec_()
#@QtCore.pyqtSlot()
[docs] def slotDocumentation(self): #docs_index = os.path.join(os.path.dirname(__file__), '../docs/html/index.html') docs_index = 'http://www.daetools.com/docs/index.html' webbrowser.open(docs_index)
[docs]def daeStartPlotter(port = 0): try: if port == 0: cfg = daeGetConfig() port = cfg.GetInteger("daetools.datareporting.tcpipDataReceiverPort", 50000) tcpipServer = daeTCPIPDataReceiverServer(port) tcpipServer.Start() if(tcpipServer.IsConnected() == False): return app = QtWidgets.QApplication(sys.argv) main = daeMainWindow(tcpipServer) main.show() app.exec_() tcpipServer.Stop() except RuntimeError: return
if __name__ == "__main__": if (len(sys.argv) > 1): daeStartPlotter(int(sys.argv[1])) else: daeStartPlotter()