#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''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/>.
Pychemqt include a basic cost stimation utility for equipments. The cost are \
updated using the Chemical Engineering Plant Cost Index (CEPCI) update \
monthly at https://www.chemengonline.com. The index define several kind of \
equipments with different update values.
.. image:: images/costIndex.png
:alt: costIndex
The module include all related cost index functionality:
* :class:`UI_CostIndex`: Dialog to show/configure costindex values
* :class:`CostData`: Common widget for equipment with cost support
API reference
-------------
'''
from functools import partial
import os
from tools.qt import QtCore, QtWidgets
from UI.widgets import Entrada_con_unidades
from lib.config import conf_dir
indiceBase = ["Jan-1982", 313.95, 336.19, 326.01, 312.03, 383.18, 297.63,
421.1, 235.42, 338.2, 263.92, 290.13, 303.26]
# Load data from config file
indiceActual = []
with open(os.path.join(conf_dir, "CostIndex.dat"), "r") as costFile:
indiceActual.append(costFile.readline()[:-1])
while True:
data = costFile.readline().rstrip("\n")
if data:
indiceActual.append(float(data))
if len(indiceActual) == 13:
break
[docs]
class Ui_CostIndex(QtWidgets.QDialog):
"""Dialog to show/configure costIndex"""
[docs]
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle(self.tr("Cost Index"))
self.custom = True
layout = QtWidgets.QGridLayout(self)
self.fecha = QtWidgets.QComboBox()
layout.addWidget(self.fecha, 1, 1, 1, 3)
layout.addWidget(QtWidgets.QLabel(self.tr("CE INDEX")), 2, 1, 1, 2)
self.index = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.index, 2, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(self.tr("Equipments")), 3, 1, 1, 2)
self.equipos = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.equipos, 3, 3, 1, 1)
layout.addItem(QtWidgets.QSpacerItem(
30, 0, QtWidgets.QSizePolicy.Policy.Fixed,
QtWidgets.QSizePolicy.Policy.Fixed), 4, 1, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Heat exchangers & Tanks")), 4, 2, 1, 1)
self.cambiadores_calor = Entrada_con_unidades(
float, width=70, decimales=1)
layout.addWidget(self.cambiadores_calor, 4, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Process machinery")), 5, 2, 1, 1)
self.maquinaria = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.maquinaria, 5, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Pipe, valves & fittings")), 6, 2, 1, 1)
self.tuberias = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.tuberias, 6, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Process instruments")), 7, 2, 1, 1)
self.instrumentos = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.instrumentos, 7, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Pumps & compressors")), 8, 2, 1, 1)
self.bombas = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.bombas, 8, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Electrical equipments")), 9, 2, 1, 1)
self.equipos_electricos = Entrada_con_unidades(
float, width=70, decimales=1)
layout.addWidget(self.equipos_electricos, 9, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Structural supports & misc")), 10, 2, 1, 1)
self.soportes = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.soportes, 10, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Construction labor")), 11, 1, 1, 2)
self.construccion = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.construccion, 11, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Buildings")), 12, 1, 1, 2)
self.edificios = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.edificios, 12, 3, 1, 1)
layout.addWidget(QtWidgets.QLabel(
self.tr("Engineering & supervision")), 13, 1, 1, 2)
self.ingenieria = Entrada_con_unidades(float, width=70, decimales=1)
layout.addWidget(self.ingenieria, 13, 3, 1, 1)
self.buttonBox = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.StandardButton.Ok
| QtWidgets.QDialogButtonBox.StandardButton.Cancel)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
layout.addWidget(self.buttonBox, 14, 1, 1, 3)
# Fill all widget data
self.indices = []
fdata = os.path.join(os.environ["pychemqt"], "dat", "costindex.dat")
with open(fdata) as archivo:
texto = archivo.readlines()
for txt in texto:
dato = txt.split()
self.fecha.insertItem(0, dato[0])
self.indices.insert(0, dato[1:])
fecha = indiceActual[0]
self.index.setValue(indiceActual[1])
self.equipos.setValue(indiceActual[2])
self.cambiadores_calor.setValue(indiceActual[3])
self.maquinaria.setValue(indiceActual[4])
self.tuberias.setValue(indiceActual[5])
self.instrumentos.setValue(indiceActual[6])
self.bombas.setValue(indiceActual[7])
self.equipos_electricos.setValue(indiceActual[8])
self.soportes.setValue(indiceActual[9])
self.construccion.setValue(indiceActual[10])
self.edificios.setValue(indiceActual[11])
self.ingenieria.setValue(indiceActual[12])
if fecha:
self.fecha.setCurrentIndex(self.fecha.findText(fecha))
else:
self.fecha.setCurrentIndex(-1)
self.fecha.currentIndexChanged.connect(self.loadData)
[docs]
def setCustom(self):
"""Set custom currentIndex"""
self.fecha.setCurrentIndex(-1)
self.custom = True
[docs]
def loadData(self, i):
"""Load costIndex data from file"""
self.index.setValue(float(self.indices[i][0]))
self.equipos.setValue(float(self.indices[i][1]))
self.cambiadores_calor.setValue(float(self.indices[i][2]))
self.maquinaria.setValue(float(self.indices[i][3]))
self.tuberias.setValue(float(self.indices[i][4]))
self.instrumentos.setValue(float(self.indices[i][5]))
self.bombas.setValue(float(self.indices[i][6]))
self.equipos_electricos.setValue(float(self.indices[i][7]))
self.soportes.setValue(float(self.indices[i][8]))
self.construccion.setValue(float(self.indices[i][9]))
self.edificios.setValue(float(self.indices[i][10]))
self.ingenieria.setValue(float(self.indices[i][11]))
self.custom = False
[docs]
def closeEvent(self, event):
"""Override close event to ask data changes"""
dialog = QtWidgets.QMessageBox.question(
self,
self.tr("Unsaved changes"),
self.tr("Save unsaved changes?"),
QtWidgets.QMessageBox.StandardButton.Yes
| QtWidgets.QMessageBox.StandardButton.No,
QtWidgets.QMessageBox.StandardButton.Yes)
if dialog == QtWidgets.QMessageBox.StandardButton.Yes:
self.accept()
else:
event.accept()
[docs]
def accept(self):
"""Overwrite accept signal to save changes"""
with open(os.path.join(conf_dir, "CostIndex.dat"), "w") as file:
if self.custom:
print()
print("custom", file=file)
else:
print(self.fecha.currentText(), file=file)
print(self.index.value, file=file)
print(self.equipos.value, file=file)
print(self.cambiadores_calor.value, file=file)
print(self.maquinaria.value, file=file)
print(self.tuberias.value, file=file)
print(self.instrumentos.value, file=file)
print(self.bombas.value, file=file)
print(self.equipos_electricos.value, file=file)
print(self.soportes.value, file=file)
print(self.construccion.value, file=file)
print(self.edificios.value, file=file)
print(self.ingenieria.value, file=file)
QtWidgets.QDialog.accept(self)
[docs]
class CostData(QtWidgets.QWidget):
"""Common widget for equipment with cost support
It have property to easy access to properties:
* factor: install factor
* base: base index (January 1982)
* actual: current index
* values: a tuple with all properties, (factor, base,actual)
"""
valueChanged = QtCore.pyqtSignal(str, float)
[docs]
def __init__(self, equipment, parent=None):
"""
equipment: equipment class where the widget have to be put, define
indiceCostos as a index in costIndex
"""
super().__init__(parent)
self.indice = equipment.indiceCostos
factor = equipment.kwargs["f_install"]
gridLayout = QtWidgets.QGridLayout(self)
gridLayout.addItem(QtWidgets.QSpacerItem(
20, 20, QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Expanding), 1, 0, 1, 7)
gridLayout.addWidget(QtWidgets.QLabel(
self.tr("Instalation factor:")), 2, 0)
self.factorInstalacion = Entrada_con_unidades(
float, spinbox=True, decimales=1, step=0.1, width=50, value=factor)
self.factorInstalacion.valueChanged.connect(partial(
self.valueChanged.emit, "f_install"))
gridLayout.addWidget(self.factorInstalacion, 2, 1)
gridLayout.addWidget(QtWidgets.QLabel(self.tr("Base index:")), 2, 4)
self.indiceBase = Entrada_con_unidades(
float, readOnly=True, value=indiceBase[self.indice], decimales=1)
gridLayout.addWidget(self.indiceBase, 2, 5)
gridLayout.addWidget(QtWidgets.QLabel(self.tr("Current index:")), 3, 4)
self.indiceActual = Entrada_con_unidades(
float, readOnly=True, colorReadOnly="white",
value=indiceActual[self.indice], decimales=1)
gridLayout.addWidget(self.indiceActual, 3, 5)
self.costIndex = QtWidgets.QToolButton()
self.costIndex.setFixedSize(QtCore.QSize(24, 24))
self.costIndex.clicked.connect(self.on_costIndex_clicked)
self.costIndex.setText("...")
self.costIndex.setVisible(False)
gridLayout.addWidget(self.costIndex, 3, 5)
gridLayout.addItem(QtWidgets.QSpacerItem(
20, 20, QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Expanding), 4, 0, 1, 7)
[docs]
def on_costIndex_clicked(self):
"""Show costIndes dialog to show/change"""
dialog = Ui_CostIndex()
if dialog.exec():
with open(os.path.join(conf_dir, "CostIndex.dat"), "r") as file:
self.indiceActual.setValue(
float(file.readlines()[self.indice][:-1]))
self.valueChanged.emit("Current_index", self.indiceActual.value)
[docs]
def enterEvent(self, event=None):
"""Show costIndex button when mouse enter in widget"""
self.costIndex.setVisible(True)
[docs]
def leaveEvent(self, event=None):
"""Hide costIndex button when mouse leave widget"""
self.costIndex.setVisible(False)
@property
def factor(self):
"""Instalation factor value property"""
return self.factorInstalacion.value
[docs]
def setFactor(self, value):
"""Set instalation factor value property"""
self.factorInstalacion.setValue(value)
@property
def base(self):
"""Base index value"""
return self.indiceBase.value
[docs]
def setBase(self, value):
"""Set base index value"""
self.indiceBase.setValue(value)
@property
def actual(self):
"""Actual index value"""
return self.indiceActual.value
[docs]
def setActual(self, value):
"""Set actual index value"""
self.indiceActual.setValue(value)
@property
def values(self):
"""Tuple with the instalation factor, the base and actual index"""
return (self.factorInstalacion.value, self.indiceBase.value,
self.indiceActual.value)
[docs]
def setValues(self, factor, base, actual):
"""Set values property"""
self.factorInstalacion.setValue(factor)
self.indiceBase.setValue(base)
self.indiceActual.setValue(actual)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
CostIndex = Ui_CostIndex()
CostIndex.show()
sys.exit(app.exec())