#!/usr/bin/python3
# -*- coding: utf-8 -*-
# pylint: disable=too-many-locals, protected-access
'''Pychemqt, Chemical Engineering Process simulator
Copyright (C) 2009-2025, Juan José Gómez Romera <jjgomera@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.'''
###############################################################################
# Table functionality for plugin:
# - TablaMEoS: Tabla subclass to show meos data, add context menu options
# - Ui_Saturation: Dialog to define input for a two-phase table calculation
# - Ui_Isoproperty: Dialog to define input for isoproperty table calculations
# - AddPoint: Dialog to add new point to line2D
# - createTabla: create TablaMEoS
###############################################################################
from configparser import ConfigParser
from functools import partial
from math import ceil, floor
import os
from numpy import delete, insert
from tools.qt import QtCore, QtGui, QtWidgets, translate
from lib import meos, mEoS, coolProp, unidades, config
from lib.thermo import ThermoAdvanced
from lib.utilities import representacion, exportTable
from UI.widgets import (Entrada_con_unidades, createAction, Status, Tabla,
NumericFactor, ClickableLabel)
from .chooseFluid import Dialog_InfoFluid
from .library import getClassFluid, getMethod, get_propiedades, _getData
from .plot import PlotMEoS
# Table data
[docs]
def createTabla(conf, title, fluidos=None, parent=None):
"""Create TablaMEoS to add to mainwindow
Parameters
----------
conf : ConfigParser
configparser instance with project configuration
title : str
title for the table
fluidos : lib.MEoS
optional array with meos instances to fill de table
parent : object
mainwindow pointer
"""
propiedades, keys, units = get_propiedades(conf)
# Add the unit suffix to properties title
for i, unit in enumerate(units):
sufx = unit.text()
if not sufx:
sufx = "[-]"
propiedades[i] = propiedades[i]+os.linesep+sufx
# Add two phases properties if requested
if conf.getboolean("MEoS", "phase"):
for i in range(len(propiedades)-1, -1, -1):
if keys[i] in ThermoAdvanced.propertiesPhase():
txt = [propiedades[i]]
prefix = translate("UI_Tables", "Liquid")
txt.append(prefix+os.linesep+propiedades[i])
prefix = translate("UI_Tables", "Vapour")
txt.append(prefix+os.linesep+propiedades[i])
propiedades[i:i+1] = txt
units[i:i+1] = [units[i]]*3
# Define common argument for TableMEoS
kw = {}
kw["horizontalHeader"] = propiedades
kw["stretch"] = False
kw["units"] = units
kw["parent"] = parent
kw["keys"] = keys
if fluidos:
# Generate a readOnly table filled of data
tabla = TablaMEoS(len(propiedades), readOnly=True, **kw)
data = []
for fluido in fluidos:
fila = _getData(fluido, keys, conf.getboolean("MEoS", "phase"))
data.append(fila)
tabla.setData(data)
else:
# Generate a dinamic table empty
columnInput = []
for key in keys:
if key in ["P", "T", "x", "rho", "v", "h", "s"]:
columnInput.append(False)
else:
columnInput.append(True)
if conf.getboolean("MEoS", "phase") and \
key in ThermoAdvanced.propertiesPhase():
columnInput.append(True)
columnInput.append(True)
kw["columnReadOnly"] = columnInput
kw["dinamica"] = True
# Discard the keys from single phase state as input values
if conf.getboolean("MEoS", "phase"):
for i in range(len(keys)-1, -1, -1):
if keys[i] in ThermoAdvanced.propertiesPhase():
keys[i:i+1] = [keys[i], "", ""]
tabla = TablaMEoS(len(propiedades), filas=1, **kw)
prefix = translate("UI_Tables", "Table")
tabla.setWindowTitle(prefix+": "+title)
tabla.resizeColumnsToContents()
state = {}
state["method"] = getMethod()
state["fluid"] = conf.getint("MEoS", "fluid")
tabla.changeStatusThermo(state)
return tabla
[docs]
class TablaMEoS(Tabla):
"""Tabla customize to show meos data, add context menu options, save and
load support in project"""
Plot = None
icon = os.path.join(config.IMAGE_PATH, "button", "table.png")
Point = None
[docs]
def __init__(self, *args, **kwargs):
"""Constructor with additional kwargs don't recognize in Tabla
keys: array with keys properties
units: array of unidades subclasses
orderUnit: array of index of unit magnitud to show
format: array of dict with numeric format
"""
# Manage special parameter dont recognize in Tabla
self.parent = kwargs.get("parent", None)
if "keys" in kwargs:
self.keys = kwargs["keys"]
del kwargs["keys"]
else:
self.keys = []
self.units = kwargs["units"]
del kwargs["units"]
if "orderUnit" in kwargs:
self.orderUnit = kwargs["orderUnit"]
del kwargs["orderUnit"]
else:
self.orderUnit = []
for unit in self.units:
if unit == unidades.Dimensionless:
self.orderUnit.append(0)
else:
conf = self.parent.currentConfig
self.orderUnit.append(conf.getint('Units', unit.__name__))
if "format" in kwargs:
self.format = kwargs["format"]
del kwargs["format"]
else:
self.format = [
{"fmt": 1, "decimales": 6, "signo": False}]*args[0]
super().__init__(*args, **kwargs)
self.setWindowIcon(QtGui.QIcon(QtGui.QPixmap(self.icon)))
self.horizontalHeader().setContextMenuPolicy(
QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.horizontalHeader().customContextMenuRequested.connect(
self.hHeaderClicked)
self.verticalHeader().setContextMenuPolicy(
QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
self.verticalHeader().customContextMenuRequested.connect(
self.vHeaderClicked)
self.itemSelectionChanged.connect(self.selectPoint)
self.data = []
if not self.readOnly:
self.cellChanged.connect(self.calculatePoint)
# Widgets to show in the statusbar of mainwindow
self.statusWidget = []
self.statusThermo = ClickableLabel()
self.statusThermo.setFrameShape(QtWidgets.QFrame.Shape.WinPanel)
self.statusThermo.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
self.statusThermo.clicked.connect(self.showFluid)
self.statusWidget.append(self.statusThermo)
[docs]
def showFluid(self):
"""Show fluid info dialog"""
dlg = Dialog_InfoFluid(self.Point.__class__)
dlg.exec()
[docs]
def changeStatusThermo(self, conf):
"""Change text show in status of mainwindow"""
fluid = getClassFluid(conf["method"], conf["fluid"])
txt = f"{fluid.name} ({conf['method']})"
self.statusThermo.setText(txt)
[docs]
def closeEvent(self, event):
"""Force project changes to save at exit"""
self.parent.dirty[self.parent.idTab] = True
self.parent.saveControl()
[docs]
def _getPlot(self):
"""Return plot if it's loaded"""
# FIXME: This procedure detect the first PlotMeos window, correct or
# incorrect
if not self.Plot:
wdws = self.parent.centralWidget().currentWidget().subWindowList()
for window in wdws:
widget = window.widget()
if isinstance(widget, PlotMEoS):
self.Plot = widget
break
return self.Plot
[docs]
def delete(self, rows):
"""Delete rows from table and for saved data"""
self.parent.statusBar().showMessage(self.tr("Deleting point..."))
QtWidgets.QApplication.processEvents()
# Delete point from table
for row in rows:
self.removeRow(row)
delete(self.data, row)
# Update verticalHeader
for row in range(self.rowCount()):
self.setVHeader(row)
# Delete point from data plot
plot = self._getPlot()
if plot:
data = plot._getData()
pref = self.tr("Table from")
title = self.windowTitle().split(pref)[1][1:]
for row in rows:
if title == self.tr("Melting Line"):
for x in ThermoAdvanced.propertiesKey():
del data["melting"][x][row]
elif title == self.tr("Sublimation Line"):
for x in ThermoAdvanced.propertiesKey():
del data["sublimation"][x][row]
elif title == self.tr("Saturation Line") or \
title == self.tr("Liquid Saturation Line"):
for x in ThermoAdvanced.propertiesKey():
del data["saturation_0"][x][row]
elif title == self.tr("Vapor Saturation Line"):
for x in ThermoAdvanced.propertiesKey():
del data["saturation_1"][x][row]
else:
units = {"P": unidades.Pressure,
"T": unidades.Temperature,
"h": unidades.Enthalpy,
"s": unidades.Enthalpy,
"v": unidades.SpecificVolume,
"rho": unidades.Density}
var = str(title.split(" = ")[0])
txt = title.split(" = ")[1]
unit = units[var]
value = float(txt.split(" ")[0])
stdValue = str(unit(value, "conf"))
for x in ThermoAdvanced.propertiesKey():
del data[var][stdValue][x][row]
plot._saveData(data)
# Delete point from data
for line in plot.plot.ax.lines:
if str(line.get_label()) == str(title):
xdata = line._x
ydata = line._y
for row in rows:
xdata = delete(xdata, row)
ydata = delete(ydata, row)
line.set_xdata(xdata)
line.set_ydata(ydata)
plot.plot.draw()
break
self.parent.statusBar().clearMessage()
[docs]
def add(self, row):
"""Add point to a table and to saved file"""
pref = self.tr("Table from ")
if pref in self.windowTitle():
title = self.windowTitle().split(pref)[1]
melting = title == self.tr("Melting Line")
else:
melting = False
dlg = AddPoint(self.Point._new(), melting, self.parent)
if dlg.exec():
self.blockSignals(True)
if dlg.checkBelow.isChecked():
row += 1
plot = self.Plot
if plot is None:
plot = self._getPlot()
if plot is None:
# If table has no associated plot, define as normal point
units = []
for ui, order in zip(self.units, self.orderUnit):
if ui is unidades.Dimensionless:
units.append("")
else:
units.append(ui.__units__[order])
phase = self.parent.currentConfig.getboolean("MEoS", "phase")
datatoTable = _getData(dlg.fluid, self.keys, phase, units)
else:
# If table has a associated plot, use the values of that
datatoTable = []
datatoTable.append(getattr(dlg.fluid, plot.x).config())
datatoTable.append(getattr(dlg.fluid, plot.y).config())
# Add point to table
self.addRow(index=row)
self.setRow(row, datatoTable)
# Update verticalHeader
for title in range(self.rowCount()):
self.setVHeader(title)
# Add point to data plot
if plot is None:
return
data = plot._getData()
if title == self.tr("Melting Line"):
for x in ThermoAdvanced.propertiesKey():
data["melting"][x].insert(
row, getattr(dlg.fluid, x))
elif title == self.tr("Sublimation Line"):
for x in ThermoAdvanced.propertiesKey():
data["sublimation"][x].insert(
row, getattr(dlg.fluid, x))
elif title == self.tr("Saturation Line") or \
title == self.tr("Liquid Saturation Line"):
for x in ThermoAdvanced.propertiesKey():
data["saturation_0"][x].insert(
row, getattr(dlg.fluid, x))
elif title == self.tr("Vapor Saturation Line"):
for x in ThermoAdvanced.propertiesKey():
data["saturation_1"][x].insert(
row, getattr(dlg.fluid, x))
else:
units = {"P": unidades.Pressure,
"T": unidades.Temperature,
"h": unidades.Enthalpy,
"s": unidades.Enthalpy,
"v": unidades.SpecificVolume,
"rho": unidades.Density}
var = str(title.split(" = ")[0])
txt = title.split(" = ")[1]
unit = units[var]
value = float(txt.split(" ")[0])
stdValue = str(unit(value, "conf"))
for x in ThermoAdvanced.propertiesKey():
data[var][stdValue][x].insert(
row, getattr(dlg.fluid, x))
plot._saveData(data)
# Add point to data
for line in plot.plot.ax.lines:
if str(line.get_label()) == str(title):
xdata = insert(line._x, row, datatoTable[0])
ydata = insert(line._y, row, datatoTable[1])
line.set_xdata(xdata)
line.set_ydata(ydata)
plot.plot.draw()
break
self.blockSignals(False)
[docs]
def selectPoint(self):
"""Show selected point in table in asociated plot if exist"""
if self.dinamica:
return
plot = self._getPlot()
if plot:
# Remove old selected point if exist
for i, line in enumerate(plot.plot.ax.lines):
if line.get_label() == self.tr("Selected Point"):
del line
del plot.plot.ax.lines[i]
# Add new selected points
x = []
y = []
for item in self.selectedItems():
if item.column():
y.append(float(item.text()))
else:
x.append(float(item.text()))
label = self.tr("Selected Point")
plot.plot.ax.plot(x, y, 'ro', label=label)
plot.plot.draw()
[docs]
def calculatePoint(self, row, column):
"""Add new value to kwargs for point, and show properties if it is
calculable
row, column: index for modified cell in table"""
txt = self.item(row, column).text()
if not txt:
return
key = self.keys[column]
unit = self.units[column]
if unit is unidades.Dimensionless:
value = float(self.item(row, column).text())
else:
data = float(self.item(row, column).text())
value = unit(data, unit.__units__[self.orderUnit[column]])
self.Point(**{key: value})
# If the Point is calculated, get data
if self.Point.status:
units = []
for ui, order in zip(self.units, self.orderUnit):
if ui is unidades.Dimensionless:
units.append("")
else:
units.append(ui.__units__[order])
phase = self.parent.currentConfig.getboolean("MEoS", "phase")
data = _getData(self.Point, self.keys, phase, units)
self.setRow(row, data)
self.Point = self.Point._new()
self.addRow()
self.setCurrentCell(row+1, column)
[docs]
def setData(self, data):
"""Override Tabla method to adapt functionality"""
if self.readOnly:
self.data = data
self.setStr()
else:
for i, row in enumerate(data):
self.setRow(i, row)
self.resizeColumnsToContents()
[docs]
def setStr(self):
"""Add data as string to cell table"""
for fila, array in enumerate(self.data):
if fila >= self.rowCount():
self.addRow()
for columna, data in enumerate(array):
if isinstance(data, str):
txt = data
else:
txt = representacion(data, **self.format[columna])
self.setValue(fila, columna, txt)
[docs]
def setRow(self, row, data):
"""Add data to a row"""
self.blockSignals(True)
self.data.insert(row, data)
for column, dat in enumerate(data):
if isinstance(dat, str):
txt = dat
else:
txt = representacion(dat, **self.format[column])
self.setValue(row, column, txt)
self.resizeColumnsToContents()
# Set calculate point readOnly
if not self.readOnly:
color = config.Preferences.get("General", 'Color_ReadOnly')
for i, boolean in enumerate(self.columnReadOnly):
if not boolean:
self.item(row, i).setFlags(
QtCore.Qt.ItemFlag.ItemIsEnabled
| QtCore.Qt.ItemFlag.ItemIsSelectable)
self.item(row, i).setBackground(QtGui.QColor(color))
self.blockSignals(False)
[docs]
def copy(self, event=None):
"""Copy selected values to clipboard"""
txt = [w.text() for w in self.selectedItems()]
QtWidgets.QApplication.clipboard().setText(" ".join(txt))
[docs]
def exportCSV(self):
"""Export data table as a csv file"""
if self.parent.currentFilename:
folder = os.path.dirname(str(self.parent.currentFilename))
else:
folder = "."
pat = []
pat.append(self.tr("CSV files") + " (*.csv)")
if os.environ["ezodf"] == "True":
pat.append(self.tr("Libreoffice spreadsheet files") + " (*.ods)")
if os.environ["xlwt"] == "True":
pat.append(self.tr("Microsoft Excel 97/2000/XP/2003 XML")
+ " (*.xls)")
if os.environ["openpyxl"] == "True":
pat.append(self.tr("Microsoft Excel 2007/2010 XML") + " (*.xlsx)")
patron = ";;".join(pat)
fname, ext = QtWidgets.QFileDialog.getSaveFileName(
self, self.tr("Export table to file"), folder, patron)
if fname and ext:
ext = ext.split(".")[-1][:-1]
exportTable(self.data, fname, ext, self.horizontalHeaderLabel)
[docs]
def writeToJSON(self, data):
"""Write instance parameter to file"""
data["column"] = self.columnCount()
# Save titles
data["title"] = self.windowTitle()
data["htitle"] = []
for column in range(data["column"]):
data["htitle"].append(self.horizontalHeaderItem(column).text())
# Save units as index
units = unidades._all
units.append(unidades.Dimensionless)
data["unit"] = [units.index(unit) for unit in self.units]
# Save method calculation options
if isinstance(self.Point, meos.MEoS):
data["method"] = "meos"
data["fluid"] = mEoS.__all__.index(self.Point.__class__)
data["external_dependences"] = ""
elif isinstance(self.Point, coolProp.CoolProp):
data["method"] = "coolprop"
data["fluid"] = mEoS.id_mEoS.index(self.Point.kwargs["ids"][0])
data["external_dependences"] = "CoolProp"
else:
data["method"] = "refprop"
data["fluid"] = mEoS.id_mEoS.index(self.Point.kwargs["ids"][0])
data["external_dependences"] = "refprop"
# Save keys
data["keys"] = self.keys
data["readOnly"] = self.readOnly
data["dinamica"] = self.dinamica
if not self.readOnly:
data["columnReadOnly"] = self.columnReadOnly
# Save order unit
data["orderUnit"] = self.orderUnit
# Save format
data["format"] = self.format
# Save data
data["data"] = list(self.data)
[docs]
@classmethod
def readFromJSON(cls, data, parent):
"""Load data table from saved file"""
# Get units
units = unidades._all
units.append(unidades.Dimensionless)
units = [units[i] for i in data["unit"]]
# Create Tabla
kwargs = {}
kwargs["horizontalHeader"] = data["htitle"]
kwargs["format"] = data["format"]
kwargs["stretch"] = False
kwargs["parent"] = parent
kwargs["units"] = units
kwargs["orderUnit"] = data["orderUnit"]
kwargs["keys"] = data["keys"]
kwargs["dinamica"] = data["dinamica"]
if data["readOnly"]:
kwargs["readOnly"] = True
else:
kwargs["filas"] = len(data["data"])+1
kwargs["columnReadOnly"] = data["columnReadOnly"]
tabla = TablaMEoS(data["column"], **kwargs)
tabla.setWindowTitle(data["title"])
tabla.setData(data["data"])
fluid = getClassFluid(data["method"], data["fluid"])
tabla.Point = fluid
tabla.changeStatusThermo(data)
return tabla
[docs]
class Ui_Saturation(QtWidgets.QDialog):
"""Dialog to define input for a two-phase saturation table calculation"""
[docs]
def __init__(self, method=None, fluid=None, parent=None):
"""
Parameters
----------
method: str
name of method of calculation, meos, coolprop or refprop
fluid: int
Index of fluid in list
"""
super().__init__(parent)
self.setWindowTitle(self.tr("Saturation Table"))
layout = QtWidgets.QGridLayout(self)
gboxType = QtWidgets.QGroupBox(self.tr("Interphase"))
layout.addWidget(gboxType, 1, 1, 1, 2)
layoutg1 = QtWidgets.QGridLayout(gboxType)
self.VL = QtWidgets.QRadioButton(
self.tr("Vapor-Liquid (boiling line)"))
layoutg1.addWidget(self.VL, 1, 1)
self.SL = QtWidgets.QRadioButton(self.tr("Solid-Liquid (melting line"))
layoutg1.addWidget(self.SL, 2, 1)
self.SV = QtWidgets.QRadioButton(
self.tr("Solid-Vapor (Sublimation line)"))
layoutg1.addWidget(self.SV, 3, 1)
groupboxVariar = QtWidgets.QGroupBox(self.tr("Change"))
layout.addWidget(groupboxVariar, 1, 3, 1, 2)
layoutg2 = QtWidgets.QGridLayout(groupboxVariar)
self.VariarTemperatura = QtWidgets.QRadioButton(self.tr("Temperature"))
self.VariarTemperatura.toggled.connect(self.updateVar)
layoutg2.addWidget(self.VariarTemperatura, 1, 1)
self.VariarPresion = QtWidgets.QRadioButton(self.tr("Pressure"))
self.VariarPresion.toggled.connect(self.updateVar)
layoutg2.addWidget(self.VariarPresion, 2, 1)
self.VariarXconT = QtWidgets.QRadioButton(
self.tr("Quality at fixed temperature"))
self.VariarXconT.toggled.connect(self.updateVar)
layoutg2.addWidget(self.VariarXconT, 3, 1)
self.VariarXconP = QtWidgets.QRadioButton(
self.tr("Quality at fixed pressure"))
self.VariarXconP.toggled.connect(self.updateVar)
layoutg2.addWidget(self.VariarXconP, 4, 1)
line = QtWidgets.QFrame()
line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
layout.addWidget(line, 2, 1, 1, 4)
self.labelFix = QtWidgets.QLabel()
layout.addWidget(self.labelFix, 4, 3)
self.variableFix = Entrada_con_unidades(float)
layout.addWidget(self.variableFix, 4, 4)
self.labelinicial = QtWidgets.QLabel(self.tr("Initial"))
layout.addWidget(self.labelinicial, 4, 1)
self.Inicial = Entrada_con_unidades(float)
layout.addWidget(self.Inicial, 4, 2)
self.labelfinal = QtWidgets.QLabel(self.tr("Final"))
layout.addWidget(self.labelfinal, 5, 1)
self.Final = Entrada_con_unidades(float)
layout.addWidget(self.Final, 5, 2)
self.labelincremento = QtWidgets.QLabel(self.tr("Increment"))
layout.addWidget(self.labelincremento, 6, 1)
self.Incremento = Entrada_con_unidades(float)
layout.addWidget(self.Incremento, 6, 2)
buttonBox = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.StandardButton.Ok
| QtWidgets.QDialogButtonBox.StandardButton.Cancel)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
layout.addWidget(buttonBox, 10, 1, 1, 4)
if method:
self.fluido = getClassFluid(method, fluid)
if isinstance(self.fluido, meos.MEoS) and (
self.fluido._Melting_Pressure != meos.MEoS._Melting_Pressure or
self.fluido._melting):
self.SL.setEnabled(True)
else:
self.SL.setEnabled(False)
if isinstance(self.fluido, meos.MEoS) and (
self.fluido._sublimation or
self.fluido._Sublimation_Pressure !=
meos.MEoS._Sublimation_Pressure):
self.SV.setEnabled(True)
else:
self.SV.setEnabled(False)
self.VL.setChecked(True)
self.VariarTemperatura.setChecked(True)
self.updateVary()
self.VL.toggled.connect(self.updateVary)
[docs]
def updateVary(self):
"""Update state for option to choose for properties to change"""
self.VariarXconP.setEnabled(self.VL.isChecked())
self.VariarXconT.setEnabled(self.VL.isChecked())
self.VariarTemperatura.setChecked(not self.VL.isChecked())
[docs]
def updateVar(self, boolean):
"""Update input values units and text"""
if boolean:
# Select initial values
fix, inicial, final, step = 0, 0, 0, 0
if self.VL.isChecked():
if self.sender() == self.VariarXconT:
fix = ceil((self.fluido.Tc-self.fluido.Tt)/2)
inicial = 0
final = 1
step = 0.1
elif self.sender() == self.VariarXconP:
fix = ceil(self.fluido.Pc/2)
inicial = 0
final = 1
step = 0.1
elif self.sender() == self.VariarTemperatura:
inicial = ceil(self.fluido.Tt)
final = floor(self.fluido.Tc)
step = 1.
self.Inicial.deleteLater()
self.Final.deleteLater()
self.Incremento.deleteLater()
if self.sender() == self.VariarXconT:
self.labelFix.setVisible(True)
self.labelFix.setText(unidades.Temperature.__title__)
self.variableFix.deleteLater()
self.variableFix = Entrada_con_unidades(
unidades.Temperature, value=fix)
self.layout().addWidget(self.variableFix, 4, 4)
unidadVariable = float
self.labelinicial.setText(self.tr("Initial quality"))
self.labelfinal.setText(self.tr("Final quality"))
elif self.sender() == self.VariarXconP:
self.labelFix.setVisible(True)
self.labelFix.setText(unidades.Pressure.__title__)
self.variableFix.deleteLater()
self.variableFix = Entrada_con_unidades(
unidades.Pressure, value=fix)
self.layout().addWidget(self.variableFix, 4, 4)
unidadVariable = float
self.labelinicial.setText(self.tr("Initial quality"))
self.labelfinal.setText(self.tr("Final quality"))
elif self.sender() == self.VariarTemperatura:
self.labelFix.setVisible(False)
self.variableFix.setVisible(False)
unidadVariable = unidades.Temperature
self.labelinicial.setText(self.tr("Initial temperature"))
self.labelfinal.setText(self.tr("Final temperature"))
else:
self.labelFix.setVisible(False)
self.variableFix.setVisible(False)
unidadVariable = unidades.Pressure
self.labelinicial.setText(self.tr("Initial pressure"))
self.labelfinal.setText(self.tr("Final pressure"))
self.Inicial = Entrada_con_unidades(unidadVariable, value=inicial)
self.Final = Entrada_con_unidades(unidadVariable, value=final)
if unidadVariable == unidades.Temperature:
unidadDelta = unidades.DeltaT
elif unidadVariable == unidades.Pressure:
unidadDelta = unidades.DeltaP
else:
unidadDelta = unidadVariable
self.Incremento = Entrada_con_unidades(unidadDelta, value=step)
self.layout().addWidget(self.Inicial, 4, 2)
self.layout().addWidget(self.Final, 5, 2)
self.layout().addWidget(self.Incremento, 6, 2)
[docs]
class Ui_Isoproperty(QtWidgets.QDialog):
"""Dialog to define input for isoproperty table calculations"""
propiedades = [
translate("UI_Tables", "Temperature"),
translate("UI_Tables", "Pressure"),
translate("UI_Tables", "Density"),
translate("UI_Tables", "Volume"),
translate("UI_Tables", "Enthalpy"),
translate("UI_Tables", "Entropy"),
translate("UI_Tables", "Internal Energy")]
unidades = [unidades.Temperature, unidades.Pressure, unidades.Density,
unidades.SpecificVolume, unidades.Enthalpy,
unidades.SpecificHeat, unidades.Enthalpy, float]
keys = ["T", "P", "rho", "v", "h", "s", "u", "x"]
[docs]
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle(self.tr("Specify Isoproperty Table"))
layout = QtWidgets.QGridLayout(self)
layout.addWidget(QtWidgets.QLabel(self.tr("Hold constant")), 1, 1)
self.fix = QtWidgets.QComboBox()
for propiedad in self.propiedades:
self.fix.addItem(propiedad)
self.fix.currentIndexChanged.connect(self.actualizarUI)
layout.addWidget(self.fix, 1, 2)
layout.addWidget(QtWidgets.QLabel(self.tr("Vary")), 2, 1)
self.vary = QtWidgets.QComboBox()
self.vary.currentIndexChanged.connect(self.actualizarVariable)
layout.addWidget(self.vary, 2, 2)
line = QtWidgets.QFrame()
line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
layout.addWidget(line, 3, 1, 1, 2)
self.labelFix = QtWidgets.QLabel()
layout.addWidget(self.labelFix, 4, 1)
self.variableFix = Entrada_con_unidades(float)
layout.addWidget(self.variableFix, 4, 2)
self.labelinicial = QtWidgets.QLabel(self.tr("Initial"))
layout.addWidget(self.labelinicial, 5, 1)
self.Inicial = Entrada_con_unidades(float)
layout.addWidget(self.Inicial, 5, 2)
self.labelfinal = QtWidgets.QLabel(self.tr("Final"))
layout.addWidget(self.labelfinal, 6, 1)
self.Final = Entrada_con_unidades(float)
layout.addWidget(self.Final, 6, 2)
self.labelincremento = QtWidgets.QLabel(self.tr("Increment"))
layout.addWidget(self.labelincremento, 7, 1)
self.Incremento = Entrada_con_unidades(float)
layout.addWidget(self.Incremento, 7, 2)
buttonBox = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.StandardButton.Ok
| QtWidgets.QDialogButtonBox.StandardButton.Cancel)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
layout.addWidget(buttonBox, 10, 1, 1, 2)
self.actualizarUI(0)
[docs]
def actualizarUI(self, indice):
"""Update UI"""
self.vary.clear()
propiedades = self.propiedades[:]
if indice <= 1:
propiedades.append(self.tr("Quality"))
del propiedades[indice]
for propiedad in propiedades:
self.vary.addItem(propiedad)
self.labelFix.setText(self.propiedades[indice])
self.variableFix.deleteLater()
self.variableFix = Entrada_con_unidades(self.unidades[indice])
self.layout().addWidget(self.variableFix, 4, 2)
[docs]
def actualizarVariable(self, indice):
"""Update UI variables"""
self.Inicial.deleteLater()
self.Final.deleteLater()
self.Incremento.deleteLater()
if indice >= self.fix.currentIndex():
indice += 1
self.Inicial = Entrada_con_unidades(self.unidades[indice])
self.Final = Entrada_con_unidades(self.unidades[indice])
if self.unidades[indice] == unidades.Temperature:
self.Incremento = Entrada_con_unidades(unidades.DeltaT)
elif self.unidades[indice] == unidades.Pressure:
self.Incremento = Entrada_con_unidades(unidades.DeltaP)
else:
self.Incremento = Entrada_con_unidades(self.unidades[indice])
self.layout().addWidget(self.Inicial, 5, 2)
self.layout().addWidget(self.Final, 6, 2)
self.layout().addWidget(self.Incremento, 7, 2)
[docs]
class AddPoint(QtWidgets.QDialog):
"""Dialog to add new point to line2D"""
keys = ["T", "P", "x", "rho", "v", "h", "s", "u"]
[docs]
def __init__(self, fluid, melting=False, parent=None):
"""
fluid: initial fluid instance
melting: boolean to add melting line calculation
"""
super().__init__(parent)
self.setWindowTitle(self.tr("Add Point to line"))
layout = QtWidgets.QGridLayout(self)
self.fluid = fluid
self.Inputs = []
for i, (title, key, unit) in enumerate(meos.inputData):
layout.addWidget(QtWidgets.QLabel(title), i, 1)
if unit is unidades.Dimensionless:
entrada = Entrada_con_unidades(float)
else:
entrada = Entrada_con_unidades(unit)
entrada.valueChanged.connect(partial(self.update, key))
self.Inputs.append(entrada)
layout.addWidget(entrada, i, 2)
self.status = Status(self.fluid.status, self.fluid.msg)
row = len(meos.inputData)
layout.addWidget(self.status, row+1, 1, 1, 2)
if isinstance(fluid, meos.MEoS) and fluid._melting:
self.checkMelting = QtWidgets.QRadioButton(
self.tr("Melting Point"))
self.checkMelting.setChecked(melting)
layout.addWidget(self.checkMelting, row+2, 1, 1, 2)
row += 1
layout.addWidget(QtWidgets.QLabel(self.tr("To")), row+2, 1)
self.To = Entrada_con_unidades(unidades.Temperature)
self.To.valueChanged.connect(partial(self.update, "To"))
layout.addWidget(self.To, row+2, 2)
layout.addWidget(QtWidgets.QLabel(self.tr("rhoo")), row+3, 1)
self.rhoo = Entrada_con_unidades(unidades.Density)
self.rhoo.valueChanged.connect(partial(self.update, "rhoo"))
layout.addWidget(self.rhoo, row+3, 2)
self.checkBelow = QtWidgets.QCheckBox(
self.tr("Add below selected point"))
layout.addWidget(self.checkBelow, row+4, 1, 1, 2)
self.buttonBox = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.StandardButton.Reset
| QtWidgets.QDialogButtonBox.StandardButton.Ok
| QtWidgets.QDialogButtonBox.StandardButton.Cancel)
self.buttonBox.clicked.connect(self.click)
layout.addWidget(self.buttonBox, row+5, 1, 1, 2)
[docs]
def click(self, button):
"""Manage mouse click event over buttonbox"""
if QtWidgets.QDialogButtonBox.StandardButton.Reset == \
self.buttonBox.standardButton(button):
self.reset()
elif QtWidgets.QDialogButtonBox.StandardButton.Ok == \
self.buttonBox.standardButton(button):
self.accept()
elif QtWidgets.QDialogButtonBox.StandardButton.Cancel == \
self.buttonBox.standardButton(button):
self.reject()
[docs]
def update(self, key, value):
"""Update fluid instance with new parameter key with value"""
self.status.setState(4)
QtWidgets.QApplication.processEvents()
if isinstance(self.fluid, meos.MEoS) and self.fluid._melting and \
self.checkMelting.isChecked() and key == "T":
P = self.fluid._Melting_Pressure(value)
self.fluid(**{key: value, "P": P})
else:
self.fluid(**{key: value})
if self.fluid.status in (1, 3):
self.fill(self.fluid)
self.status.setState(self.fluid.status, self.fluid.msg)
[docs]
def fill(self, fluid):
"""Fill dialog widget with fluid properties values"""
self.blockSignals(True)
Config = ConfigParser()
Config.read(config.conf_dir + "pychemqtrc")
for key, data in zip(self.keys, self.Inputs):
data.setValue(getattr(fluid, key))
if key in fluid.kwargs and \
fluid.kwargs[key] != fluid.__class__.kwargs[key]:
data.setResaltado(True)
else:
data.setResaltado(False)
self.blockSignals(False)
[docs]
def reset(self):
"""Reset dialog widgets to initial clear status"""
self.fluid = self.fluid.__class__()
self.status.setState(self.fluid.status, self.fluid.msg)
self.rhoo.clear()
self.To.clear()
for inputData in self.Inputs:
inputData.clear()
inputData.setResaltado(False)
if __name__ == "__main__":
import sys
from lib.mEoS import H2O
app = QtWidgets.QApplication(sys.argv)
cmp = H2O()
SteamTables = AddPoint(cmp)
SteamTables.show()
sys.exit(app.exec())