class RecoilInfoDialog(QtWidgets.QDialog, bnd.PropertyBindingWidget, metaclass=gutils.QtABCMeta): """Dialog for editing the name, description and reference density of a recoil element. """ # TODO possibly track name changes name = bnd.bind("nameLineEdit") description = bnd.bind("descriptionLineEdit") reference_density = bnd.bind("scientific_spinbox") @property def color(self): return self.tmp_color.name() def __init__(self, recoil_element: RecoilElement, colormap, element_simulation: ElementSimulation): """Inits a recoil info dialog. Args: recoil_element: A RecoilElement object. colormap: Colormap for elements. element_simulation: Element simulation that has the recoil element. """ super().__init__() self.recoil_element = recoil_element self.element_simulation = element_simulation self.tmp_color = QColor(self.recoil_element.color) self.colormap = colormap value = self.recoil_element.reference_density self.scientific_spinbox = ScientificSpinBox(value=value, minimum=0.01, maximum=9.99e23) uic.loadUi(gutils.get_ui_dir() / "ui_recoil_info_dialog.ui", self) self.okPushButton.clicked.connect(self.__accept_settings) self.cancelPushButton.clicked.connect(self.close) self.colorPushButton.clicked.connect(self.__change_color) self.fields_are_valid = True iv.set_input_field_red(self.nameLineEdit) self.nameLineEdit.textChanged.connect( lambda: iv.check_text(self.nameLineEdit, qwidget=self)) self.nameLineEdit.textEdited.connect(self.__validate) self.name = recoil_element.name self.description = recoil_element.description self.formLayout.insertRow( 4, QtWidgets.QLabel(r"Reference density [at./cm<sup>3</sup>]:"), self.scientific_spinbox) self.formLayout.removeRow(self.widget) self.description = recoil_element.description self.isOk = False self.dateLabel.setText( time.strftime( "%c %z %Z", time.localtime(self.recoil_element.modification_time))) title = f"Recoil element: " \ f"{self.recoil_element.element.get_prefix()}" self.infoGroupBox.setTitle(title) self.__set_color_button_color(self.recoil_element.element.symbol) self.exec_() def __density_valid(self): """ Check if density value is valid and in limits. Return: True or False. """ try: self.scientific_spinbox.get_value() return True except TypeError: return False def __accept_settings(self): """Function for accepting the current settings and closing the dialog window. """ if not self.fields_are_valid or not self.__density_valid(): QtWidgets.QMessageBox.critical( self, "Warning", "Some of the setting values are invalid.\n" "Please input values in fields indicated in red.", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Ok) return if self.name != self.recoil_element.name: # Check that the new name is not already in use if self.name in ( r.name for r in # has_recoil self.element_simulation.recoil_elements): QtWidgets.QMessageBox.critical( self, "Warning", "Name of the recoil element is already in use. Please use " "a different name", QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Ok) return # If current recoil is used in a running simulation if self.recoil_element is \ self.element_simulation.get_main_recoil(): if (self.element_simulation.is_simulation_running() or self.element_simulation.is_optimization_running()) and \ self.name != self.recoil_element.name: reply = QtWidgets.QMessageBox.question( self, "Recoil used in simulation", "This recoil is used in a simulation that is " "currently running.\nIf you change the name of " "the recoil, the running simulation will be " "stopped.\n\n" "Do you want to save changes anyway?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Cancel) if reply == QtWidgets.QMessageBox.No or reply == \ QtWidgets.QMessageBox.Cancel: return else: self.element_simulation.stop() self.isOk = True self.close() def __change_color(self): """ Change the color of the recoil element. """ dialog = QtWidgets.QColorDialog(self) color = dialog.getColor(self.tmp_color) if color.isValid(): self.tmp_color = color self.__change_color_button_color( self.recoil_element.element.symbol) def __change_color_button_color(self, element: str): """ Change color button's color. Args: element: String representing element name. """ df.set_btn_color(self.colorPushButton, self.tmp_color, self.colormap, element) def __set_color_button_color(self, element): """Set default color of element to color button. Args: element: String representing element. """ self.colorPushButton.setEnabled(True) self.tmp_color = QColor(self.recoil_element.color) self.__change_color_button_color(element) def __validate(self): """ Validate the recoil name. """ text = self.name regex = "^[A-Za-z0-9-ÖöÄäÅå]*" valid_text = iv.validate_text_input(text, regex) self.name = valid_text
class TestSciSpinbox(unittest.TestCase): def setUp(self): self.minimum = 1.0e20 self.maximum = 9.9e23 self.sbox = ScientificSpinBox(1.123e+22, minimum=self.minimum, maximum=self.maximum) down_btn = self.sbox.downButton up_btn = self.sbox.upButton self.click_down = lambda: QTest.mouseClick(down_btn, Qt.LeftButton) self.click_up = lambda: QTest.mouseClick(up_btn, Qt.LeftButton) def test_display(self): self.sbox.minimum = -math.inf self.sbox.set_value(5.5e+22) self.assertEqual("5.5e+22", self.sbox.scientificLineEdit.text()) self.sbox.set_value(math.pi * 1e20) self.assertEqual("3.1415926535897e+20", self.sbox.scientificLineEdit.text()) self.sbox.set_value(1.00000e21) self.assertEqual("1.0e+21", self.sbox.scientificLineEdit.text()) self.sbox.set_value(1.00000e-21) self.assertEqual("1.0e-21", self.sbox.scientificLineEdit.text()) self.sbox.set_value(-1.00000e-21) self.assertEqual("-1.0e-21", self.sbox.scientificLineEdit.text()) self.sbox.set_value(0.1) self.assertEqual("1.0e-1", self.sbox.scientificLineEdit.text()) self.sbox.set_value(0) self.assertEqual("0.0e+0", self.sbox.scientificLineEdit.text()) def test_typed_values(self): self.sbox.scientificLineEdit.setText("1.321e20") self.assertEqual(1.321e20, self.sbox.get_value()) self.sbox.scientificLineEdit.setText("0") self.assertEqual(0, self.sbox.get_value()) self.sbox.scientificLineEdit.setText("foo") self.assertRaises(TypeError, lambda: self.sbox.get_value()) def test_decrease(self): self.sbox.minimum = -math.inf self.sbox.set_value(5.5e+22) self.click_down() self.assertEqual(5.4e+22, self.sbox.get_value()) self.sbox.set_value(1.0001e+22) self.click_down() self.assertEqual(9.9001e+21, self.sbox.get_value()) self.sbox.set_value(0.0e0) self.click_down() self.assertEqual(-1.0e-2, self.sbox.get_value()) self.sbox.set_value(1.05e-3) self.click_down() self.assertEqual(9.95e-4, self.sbox.get_value()) self.sbox.set_value(-9.95e-4) self.click_down() self.assertEqual(-1.05e-3, self.sbox.get_value()) self.sbox.scientificLineEdit.setText("foo") self.click_down() self.assertEqual("foo", self.sbox.scientificLineEdit.text()) def test_increase(self): self.sbox.minimum = -math.inf self.sbox.set_value(9.81e+22) self.click_up() self.assertEqual(9.91e+22, self.sbox.get_value()) self.click_up() self.assertEqual(1.01e+23, self.sbox.get_value()) self.sbox.set_value(0.0e0) self.click_up() self.assertEqual(1.0e-2, self.sbox.get_value()) self.sbox.set_value(9.9e0) self.click_up() self.assertEqual(1.0e1, self.sbox.get_value()) self.sbox.set_value(-1.05e-10) self.click_up() self.assertEqual(-9.95e-11, self.sbox.get_value()) def test_set_value(self): self.sbox.set_value(5e22) self.assertEqual(5e22, self.sbox.get_value()) # Value below min self.sbox.set_value(10) self.assertEqual(self.minimum, self.sbox.get_value()) # Value over max self.sbox.set_value(5e25) self.assertEqual(self.maximum, self.sbox.get_value()) # Try setting a string self.sbox.set_value("5e21") self.assertEqual(5e21, self.sbox.get_value()) def test_value_properties(self): # After n up clicks and n down clicks, value of the spinbox should # be the same as it was when started self.sbox.minimum = -math.inf self.sbox.maximum = math.inf value_min = -10.0e-10 value_max = 10.0e-10 max_clicks = 25 n = 15 for i in range(n): value = random.uniform(value_min, value_max) self.sbox.set_value(value) x0 = self.sbox.get_value() clicks = random.randint(1, max_clicks) click_order = [ *[self.click_down] * clicks, *[self.click_up] * clicks ] random.shuffle(click_order) for cl in click_order: x1 = self.sbox.get_value() cl() x2 = self.sbox.get_value() if cl is self.click_down: self.assertLess(x2, x1) else: self.assertLess(x1, x2) self.assertEqual(x0, self.sbox.get_value())