diff --git a/src/main/python/DockWidgets/DockWidget.py b/src/main/python/DockWidgets/DockWidget.py index 51ad61a..5f67060 100644 --- a/src/main/python/DockWidgets/DockWidget.py +++ b/src/main/python/DockWidgets/DockWidget.py @@ -11,6 +11,7 @@ from PyQt5.uic import loadUiType from python.utils.ComponentSelector import * from python.utils.Graphics import * +from python.utils.NumberFormatting import format_result class BaseDockWidget(QDockWidget): """ @@ -448,7 +449,11 @@ def results_category(self,name): rowPosition = self.tableWidget.rowCount() self.tableWidget.insertRow(rowPosition) self.tableWidget.setItem(rowPosition , 0, QTableWidgetItem(obj.variables[val]['name'])) - self.tableWidget.setItem(rowPosition , 1, QTableWidgetItem(resultval)) + self.tableWidget.setItem( + rowPosition, + 1, + QTableWidgetItem(format_result(resultval)) + ) self.tableWidget.setItem(rowPosition , 2, QTableWidgetItem(obj.variables[val]['unit'])) self.tableWidget.resizeColumnsToContents() diff --git a/src/main/python/DockWidgets/DockWidgetCompressorExpander.py b/src/main/python/DockWidgets/DockWidgetCompressorExpander.py index a4c19ae..24b2101 100644 --- a/src/main/python/DockWidgets/DockWidgetCompressorExpander.py +++ b/src/main/python/DockWidgets/DockWidgetCompressorExpander.py @@ -11,6 +11,7 @@ from PyQt5.uic import loadUiType from python.utils.ComponentSelector import * from python.utils.Graphics import * +from python.utils.NumberFormatting import format_result from python.DockWidgets.DockWidget import BaseDockWidget @@ -196,7 +197,11 @@ def results_category(self,name): rowPosition = self.tableWidget.rowCount() self.tableWidget.insertRow(rowPosition) self.tableWidget.setItem(rowPosition , 0, QTableWidgetItem(obj.variables[val]['name'])) - self.tableWidget.setItem(rowPosition , 1, QTableWidgetItem(resultval)) + self.tableWidget.setItem( + rowPosition, + 1, + QTableWidgetItem(format_result(resultval)) + ) self.tableWidget.setItem(rowPosition , 2, QTableWidgetItem(obj.variables[val]['unit'])) self.tableWidget.resizeColumnsToContents() diff --git a/src/main/python/DockWidgets/DockWidgetDistillationColumn.py b/src/main/python/DockWidgets/DockWidgetDistillationColumn.py index bcfcad5..80c067b 100644 --- a/src/main/python/DockWidgets/DockWidgetDistillationColumn.py +++ b/src/main/python/DockWidgets/DockWidgetDistillationColumn.py @@ -16,6 +16,7 @@ from python.utils.ComponentSelector import * from python.DockWidgets.DistillationColumnStagewiseResults import DistillationColumnStagewiseResults from python.utils.Graphics import * +from python.utils.NumberFormatting import format_result from python.DockWidgets.DockWidget import BaseDockWidget @@ -261,7 +262,11 @@ def results_category(self,name): rowPosition = self.tableWidget.rowCount() self.tableWidget.insertRow(rowPosition) self.tableWidget.setItem(rowPosition, 0, QTableWidgetItem(obj.variables[val]['name'])) - self.tableWidget.setItem(rowPosition, 1, QTableWidgetItem(resultval)) + self.tableWidget.setItem( + rowPosition, + 1, + QTableWidgetItem(format_result(resultval)) + ) self.tableWidget.setItem(rowPosition, 2, QTableWidgetItem(obj.variables[val]['unit'])) self.tableWidget.resizeColumnsToContents() @@ -377,7 +382,10 @@ def results_category(self,name): T_table = self.stage_res_table.T_table T_table.setColumnCount(1) for i in range(Nt): - T_table.setItem(i, 0, QTableWidgetItem(Stages_T[i])) + T_table.setItem( + i, 0, + QTableWidgetItem(format_result(Stages_T[i])) + ) x_pc_table = self.stage_res_table.x_pc_table x_pc_table.setColumnCount(2*Nc) @@ -387,8 +395,14 @@ def results_category(self,name): for i in range(Nt): for j in range(Nc): - x_pc_table.setItem(i, 2*j, QTableWidgetItem(Stages_x_pc[i][j][1])) - x_pc_table.setItem(i, 2 * j + 1, QTableWidgetItem(Stages_x_pc[i][j][2])) + x_pc_table.setItem( + i, 2*j, + QTableWidgetItem(format_result(Stages_x_pc[i][j][1])) + ) + x_pc_table.setItem( + i, 2*j + 1, + QTableWidgetItem(format_result(Stages_x_pc[i][j][2])) + ) for t in tables: t.resizeColumnsToContents() diff --git a/src/main/python/DockWidgets/DockWidgetMaterialStream.py b/src/main/python/DockWidgets/DockWidgetMaterialStream.py index 0f19310..3955676 100644 --- a/src/main/python/DockWidgets/DockWidgetMaterialStream.py +++ b/src/main/python/DockWidgets/DockWidgetMaterialStream.py @@ -10,7 +10,7 @@ from PyQt5.uic import loadUiType from python.utils.ComponentSelector import * from python.utils.Graphics import * - +from python.utils.NumberFormatting import format_result from python.DockWidgets.DockWidget import BaseDockWidget ui_dialog,_ = loadUiType(parentPath+'/ui/DockWidgets/DockWidgetMaterialStream.ui') @@ -335,9 +335,7 @@ def results_category(self,name): obj = self.container.fetch_object(name) def _safe_result(val): - if val.replace('.','',1).replace('-','',1).replace('e','',1).replace('+','',1).isdigit(): - return str(round(float(val), 4)) - return val + return format_result(val) d = {"Mole Fraction":"x_pc", "Mass Fraction":"xm_pc", "Mole Flow":"F_pc", "Mass Flow":"Fm_pc"} @@ -378,7 +376,7 @@ def _safe_result(val): compound_no = int(k[k.index(',') + 1]) - 1 # compound from python list self._amounts_data[ms_lst[j]][phase_no].append( - (compound_selected[compound_no], _safe_result(resultval), unit_str) + (compound_selected[compound_no], resultval, unit_str) ) if phase_no == 3 and (compound_no + 1) == len(compound_selected): @@ -449,7 +447,11 @@ def _safe_result(val): mrowPosition = self.mTableWidget.rowCount() self.mTableWidget.insertRow(mrowPosition) self.mTableWidget.setItem(mrowPosition , 0, QTableWidgetItem(obj.variables[val.split('.')[1]]['name'])) - self.mTableWidget.setItem(mrowPosition , 1, QTableWidgetItem(str(round(float(resultval),4)) if resultval.replace('.','',1).replace('-','',1).replace('e','',1).replace('+','',1).isdigit() else resultval)) + self.mTableWidget.setItem( + mrowPosition, + 1, + QTableWidgetItem(format_result(resultval)) + ) self.mTableWidget.setItem(mrowPosition , 2, QTableWidgetItem(obj.variables[val.split('.')[1]]['unit'])) self.mTableWidget.resizeColumnsToContents() @@ -581,7 +583,7 @@ def _populate_amounts_table(table, rows, basis): row_pos = table.rowCount() table.insertRow(row_pos) table.setItem(row_pos, 0, QTableWidgetItem(str(compound))) - table.setItem(row_pos, 1, QTableWidgetItem(str(value))) + table.setItem(row_pos, 1, QTableWidgetItem(format_result(value))) if show_unit: table.setItem(row_pos, 2, QTableWidgetItem(str(unit))) diff --git a/src/main/python/DockWidgets/DockWidgetShortcutColumn.py b/src/main/python/DockWidgets/DockWidgetShortcutColumn.py index 8da53ee..885c80f 100644 --- a/src/main/python/DockWidgets/DockWidgetShortcutColumn.py +++ b/src/main/python/DockWidgets/DockWidgetShortcutColumn.py @@ -11,6 +11,7 @@ from PyQt5.uic import loadUiType from python.utils.ComponentSelector import * from python.utils.Graphics import * +from python.utils.NumberFormatting import format_result from python.DockWidgets.DockWidget import BaseDockWidget @@ -152,7 +153,11 @@ def results_category(self,name): rowPosition = self.tableWidget.rowCount() self.tableWidget.insertRow(rowPosition) self.tableWidget.setItem(rowPosition , 0, QTableWidgetItem(obj.variables[val]['name'])) - self.tableWidget.setItem(rowPosition , 1, QTableWidgetItem(resultval)) + self.tableWidget.setItem( + rowPosition, + 1, + QTableWidgetItem(format_result(resultval)) + ) self.tableWidget.setItem(rowPosition , 2, QTableWidgetItem(obj.variables[val]['unit'])) self.tableWidget.resizeColumnsToContents() except Exception as e: diff --git a/src/main/python/utils/NumberFormatting.py b/src/main/python/utils/NumberFormatting.py new file mode 100644 index 0000000..eb69101 --- /dev/null +++ b/src/main/python/utils/NumberFormatting.py @@ -0,0 +1,23 @@ + +from decimal import Decimal, InvalidOperation, ROUND_HALF_UP + + +def format_result(value): + """Format numeric result values to exactly two decimal places.""" + if isinstance(value, bool): + return str(value) + + try: + number = Decimal(str(value).strip()) + + if not number.is_finite(): + return str(value) + + return str( + number.quantize( + Decimal("0.01"), + rounding=ROUND_HALF_UP + ) + ) + except (InvalidOperation, ValueError, TypeError, AttributeError): + return str(value) diff --git a/tests/test_dock_widget_material_stream.py b/tests/test_dock_widget_material_stream.py index 96a1e5f..5856326 100644 --- a/tests/test_dock_widget_material_stream.py +++ b/tests/test_dock_widget_material_stream.py @@ -56,7 +56,7 @@ def test_populate_amounts_table_mole_fraction_two_columns(self): self.assertEqual(self.table.horizontalHeaderItem(1).text(), "Value") self.assertEqual(self.table.item(0, 0).text(), "Water(chemsep)") - self.assertEqual(self.table.item(0, 1).text(), "0.5") + self.assertEqual(self.table.item(0, 1).text(), "0.50") self.assertIsNone(self.table.item(0, 2)) def test_populate_amounts_table_mass_fraction_two_columns(self): @@ -81,7 +81,7 @@ def test_populate_amounts_table_mole_flow_three_columns_with_unit(self): self.assertEqual(self.table.horizontalHeaderItem(2).text(), "Unit") self.assertEqual(self.table.item(0, 0).text(), "Water(chemsep)") - self.assertEqual(self.table.item(0, 1).text(), "50.0") + self.assertEqual(self.table.item(0, 1).text(), "50.00") self.assertEqual(self.table.item(0, 2).text(), "mol/s") def test_populate_amounts_table_mass_flow_three_columns_with_unit(self): diff --git a/tests/test_number_formatting.py b/tests/test_number_formatting.py new file mode 100644 index 0000000..0bf3d4a --- /dev/null +++ b/tests/test_number_formatting.py @@ -0,0 +1,45 @@ +import os +import sys +import unittest + +PROJECT_ROOT = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..') +) +sys.path.insert(0, os.path.join(PROJECT_ROOT, 'src', 'main')) +sys.path.insert(0, os.path.join(PROJECT_ROOT, 'src', 'main', 'python')) + +from python.utils.NumberFormatting import format_result + + +class TestNumberFormatting(unittest.TestCase): + + def test_integer(self): + self.assertEqual(format_result(12), "12.00") + + def test_float(self): + self.assertEqual(format_result(12.3), "12.30") + + def test_round_half_up(self): + self.assertEqual(format_result(12.345), "12.35") + + def test_round_small_value(self): + self.assertEqual(format_result(0.999), "1.00") + + def test_negative_number(self): + self.assertEqual(format_result(-12.345), "-12.35") + + def test_scientific_notation(self): + self.assertEqual(format_result("1.2345e-3"), "0.00") + + def test_non_numeric_string(self): + self.assertEqual(format_result("Water"), "Water") + + def test_none(self): + self.assertEqual(format_result(None), "None") + + def test_boolean(self): + self.assertEqual(format_result(True), "True") + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file