def __init__(self) -> None: super().__init__() self.bg_color = "#000000" self.line_color = "#FFFFFF" self.image = None self.clear_path() self.lsystem = Lsystem("lsystem", "", {}) # Creating l-system image object self.limage = LsystemImage(self.lsystem) self.image_drawer = LsystemImageDrawer(self.limage) self.image_drawer.finishSignal.connect(self.update_label) # Creating drawer for image into thread for multithreading self.draw_thread = QThread() self.draw_thread.started.connect(self.image_drawer.work) self.image_drawer.moveToThread(self.draw_thread) self.image_drawer.finishSignal.connect(self.draw_thread.quit) # Creating scaled l-ystem image object self.scaled_limage = LsystemImage(self.lsystem) self.scaled_image_drawer = LsystemImageDrawer(self.scaled_limage) self.scaled_image_drawer.finishSignal.connect(self.save_image) # Creating drawer for scaled image into thread self.scaled_draw_thread = QThread() self.scaled_draw_thread.started.connect(self.scaled_image_drawer.work) self.scaled_image_drawer.moveToThread(self.scaled_draw_thread) self.scaled_image_drawer.finishSignal.connect( self.scaled_draw_thread.quit) # Creatign database manager window self.db_manager = Window_db(self) # Loading ui self.initUi()
def _update_step_length(self, limage: LsystemImage, scale: int) -> None: limage.set_step_length(self.spinBox_step_length.value() * scale)
def _update_rot_angle_div(self, limage: LsystemImage) -> None: rad_angle = angle_part_of_circle(self.spinBox_plane_div.value()) limage.set_rot_angle(rad_angle)
def _update_rot_angle_angle(self, limage: LsystemImage) -> None: rad_angle = deg_to_rad(self.spinBox_angle.value()) limage.set_rot_angle(rad_angle)
def _update_start_coords(self, limage: LsystemImage, scale: int) -> None: image_x, image_y = decart_to_image_coords( (self.spinBox_start_x.value() * scale, self.spinBox_start_y.value() * scale), limage.get_size()) limage.set_start_coords(image_x, image_y)
def _update_size(self, limage: LsystemImage, scale: int) -> None: limage.set_size(self.spinBox_size_x.value() * scale, self.spinBox_size_y.value() * scale)
class MainWindow(QMainWindow, BasicUiUtils, Ui_MainWindow): def __init__(self) -> None: super().__init__() self.bg_color = "#000000" self.line_color = "#FFFFFF" self.image = None self.clear_path() self.lsystem = Lsystem("lsystem", "", {}) # Creating l-system image object self.limage = LsystemImage(self.lsystem) self.image_drawer = LsystemImageDrawer(self.limage) self.image_drawer.finishSignal.connect(self.update_label) # Creating drawer for image into thread for multithreading self.draw_thread = QThread() self.draw_thread.started.connect(self.image_drawer.work) self.image_drawer.moveToThread(self.draw_thread) self.image_drawer.finishSignal.connect(self.draw_thread.quit) # Creating scaled l-ystem image object self.scaled_limage = LsystemImage(self.lsystem) self.scaled_image_drawer = LsystemImageDrawer(self.scaled_limage) self.scaled_image_drawer.finishSignal.connect(self.save_image) # Creating drawer for scaled image into thread self.scaled_draw_thread = QThread() self.scaled_draw_thread.started.connect(self.scaled_image_drawer.work) self.scaled_image_drawer.moveToThread(self.scaled_draw_thread) self.scaled_image_drawer.finishSignal.connect( self.scaled_draw_thread.quit) # Creatign database manager window self.db_manager = Window_db(self) # Loading ui self.initUi() def initUi(self) -> None: # Loading ui from converted file self.setupUi(self) self.retranslateUi(self) # Connectiong signals self.spinBox_step.valueChanged.connect(self.update_step_slider) self.horizontalSlider_step.valueChanged.connect( self.update_step_spinbox) self.pushButton_update.clicked.connect(self.generate_result) self.pushButton_ch_bg_color.clicked.connect(self.request_bg_color) self.pushButton_ch_line_color.clicked.connect(self.request_line_color) self.pushButton_clear.clicked.connect(self.clear_label) self.radioButton_angle_mode.clicked.connect(self.set_angle_mode) self.radioButton_div_mode.clicked.connect(self.set_plane_div_mode) self.pushButton_save_image.clicked.connect(self.generate_scaled_result) self.action_open.triggered.connect(self.open) self.action_new.triggered.connect(self.new) self.action_save.triggered.connect(self.save) self.action_saveas.triggered.connect(self.saveas) self.action_exit.triggered.connect(exit) self.action_open_db_manager.triggered.connect(self.db_manager.show) self.action_save_to_db.triggered.connect(self.save_to_db) # Wrapper function for updating params def update_lsystem_params(self) -> None: self._update_name() self._update_inititator() self._update_rules() def update_limage_params(self, limage: LsystemImage, scale: int = 1) -> None: self._update_size(limage, scale) self._update_start_coords(limage, scale) self._update_start_angle(limage) if self.radioButton_angle_mode.isChecked(): self._update_rot_angle_angle(limage) else: self._update_rot_angle_div(limage) self._update_step_length(limage, scale) def update_drawer_params(self, drawer: LsystemImageDrawer) -> None: self._update_iteration(drawer) def _update_name(self) -> None: self.lsystem.set_name(self.lineEdit_name.text()) def _update_inititator(self) -> None: self.lsystem.set_inititator(self.lineEdit_initiator.text()) def _update_rules(self) -> None: # Checking if rules is not empty try: rules_dict = strings_to_dict( self.plainTextEdit_rules.toPlainText().split("\n"), " ") except IndexError: # On empty rules dont change anything return self.lsystem.set_rules(rules_dict) def _update_size(self, limage: LsystemImage, scale: int) -> None: limage.set_size(self.spinBox_size_x.value() * scale, self.spinBox_size_y.value() * scale) def _update_start_coords(self, limage: LsystemImage, scale: int) -> None: image_x, image_y = decart_to_image_coords( (self.spinBox_start_x.value() * scale, self.spinBox_start_y.value() * scale), limage.get_size()) limage.set_start_coords(image_x, image_y) def _update_start_angle(self, limage: LsystemImage) -> None: rad_angle = deg_to_rad(self.spinBox_start_angle.value()) limage.set_start_angle(rad_angle) def _update_rot_angle_angle(self, limage: LsystemImage) -> None: rad_angle = deg_to_rad(self.spinBox_angle.value()) limage.set_rot_angle(rad_angle) def _update_rot_angle_div(self, limage: LsystemImage) -> None: rad_angle = angle_part_of_circle(self.spinBox_plane_div.value()) limage.set_rot_angle(rad_angle) def _update_step_length(self, limage: LsystemImage, scale: int) -> None: limage.set_step_length(self.spinBox_step_length.value() * scale) def _update_iteration(self, drawer: LsystemImageDrawer) -> None: drawer.set_iteration(self.spinBox_step.value()) def new(self) -> None: self.clear_path() self.spinBox_angle.setValue(0) self.spinBox_plane_div.setValue(2) self.spinBox_start_angle.setValue(0) self.spinBox_step.setValue(1) self.horizontalSlider_step.setValue(1) self.lineEdit_name.clear() self.lineEdit_initiator.clear() self.plainTextEdit_rules.clear() self.clear_label() def save_data(self) -> None: name = self.lineEdit_name.text() if not name: name = "lsystem" if self.radioButton_angle_mode.isChecked(): angle_div = self.spinBox_angle.value() else: angle_div = self.spinBox_plane_div.value() initiator = self.lineEdit_initiator.text() rules_text = self.plainTextEdit_rules.toPlainText() with open(self.path, "w") as f: f.write(name + "\n") f.write(str(angle_div) + "\n") f.write(initiator + "\n") f.write(rules_text + "\n") def save(self) -> None: if self.path is not None: self.save_data() else: self.saveas() def saveas(self) -> None: # Checking if file selected try: self.path = self.requset_save_path( "Сохранить l-систему", "l-system files (*.ls)") self.save_data() except FileNotFoundError: # Doing nothing if no file selected return def open(self) -> None: # Checking if file selected try: self.path = self.request_open_path( "Открыть l-систему", "l-system files (*.ls)") except FileNotFoundError: # Doing nothing if no file selected return # Loading data if file selected self.load_data() def load_data(self) -> None: with open(self.path, "r") as f: text = f.read() lines = text.strip().split("\n") lines = tuple(line.strip() for line in lines) # Checking for file format if not (lines[1].isdecimal() and all(map(lambda line: " " in line, lines[3:]))): self.clear_path() self.show_messagebox("Неверный формат файла!") return try: # Asking for loading mode mode = self.request_choice( "Как загрузить данные", "Выберите режим поворотов", ("Деление плоскости", "Угол")) except InterruptedError: # Clearing path and exiting if mode not selected self.clear_path() return name = lines[0] angle_div = int(lines[1]) initiator = lines[2] rules_text = "\n".join(lines[3:]) if mode == "Угол": self.set_angle_mode() self.spinBox_angle.setValue(angle_div) else: self.set_plane_div_mode() self.spinBox_plane_div.setValue(angle_div) self.lineEdit_name.setText(name) self.lineEdit_initiator.setText(initiator) self.plainTextEdit_rules.setPlainText(rules_text) def clear_path(self) -> None: self.path = None def request_line_color(self) -> None: try: color = self.request_color() except ValueError: return self.limage.set_line_color(color) self.scaled_limage.set_line_color(color) def request_bg_color(self) -> None: try: color = self.request_color() except ValueError: return self.limage.set_bg_color(color) self.scaled_limage.set_bg_color(color) def generate_result(self) -> None: self.update_lsystem_params() self.update_limage_params(self.limage) self.update_drawer_params(self.image_drawer) self.draw_thread.start() def check_values(self) -> bool: return self.lineEdit_initiator.text() and self.plainTextEdit_rules.toPlainText() def get_rule_strings(self) -> list: text = self.plainTextEdit_rules.toPlainText() return text.split("\n") def update_step_slider(self) -> None: self.horizontalSlider_step.setValue(self.spinBox_step.value()) def update_step_spinbox(self) -> None: self.spinBox_step.setValue(self.horizontalSlider_step.value()) def update_label(self) -> None: image = self.limage.get_image() pixmap = image_to_pixmap(image) self.label_result.setPixmap(pixmap) def clear_label(self) -> None: self.label_result.clear() def set_angle_mode(self) -> None: self.radioButton_angle_mode.setChecked(True) self.spinBox_angle.setEnabled(True) self.spinBox_plane_div.setEnabled(False) def set_plane_div_mode(self) -> None: self.radioButton_div_mode.setChecked(True) self.spinBox_angle.setEnabled(False) self.spinBox_plane_div.setEnabled(True) def save_image(self) -> None: try: path = self.requset_save_path("Сохранить", "All files (*.*)") except FileNotFoundError: return img = self.scaled_limage.get_image() img = img.convert("RGBA") img.save(path) def generate_scaled_result(self) -> None: scale = self.spinBox_scale.value() self.update_lsystem_params() self.update_limage_params(self.scaled_limage, scale) self.update_drawer_params(self.scaled_image_drawer) self.scaled_draw_thread.start() def save_to_db(self): name = self.lineEdit_name.text() # Checking name if not name: name = "lsystem" if self.radioButton_angle_mode.isChecked(): angle_div = self.spinBox_angle.value() else: angle_div = self.spinBox_plane_div.value() # Loading params initiator = self.lineEdit_initiator.text() rules_text = self.plainTextEdit_rules.toPlainText() if self.radioButton_angle_mode.isChecked(): rot_angle = self.spinBox_angle.value() plane_div = "" else: rot_angle = "" plane_div = self.spinBox_plane_div.value() self.db_manager.create_cursor() # Checking is same system exists id_with_name = self.db_manager.execute_query_fetchone( f"SELECT id FROM lsystems WHERE name = '{name}'") if id_with_name: # Asking for overwrite ans = QMessageBox.question(self, "Перезапись", "Такая система уже сохранена в БД, перезаписать?", QMessageBox.Yes, QMessageBox.No) if ans == QMessageBox.Yes: self.db_manager.execute_query(f"""UPDATE lsystems SET initiator = '{initiator}', rules = '{rules_text}', \ rot_angle = '{rot_angle}', plane_div = '{plane_div}' WHERE id = {id_with_name[0]}""") else: return else: self.db_manager.execute_query(f"""INSERT INTO lsystems(name, initiator, rules, rot_angle, plane_div) \ VALUES('{name}', '{initiator}', '{rules_text}', '{rot_angle}', '{plane_div}')""") self.db_manager.close_cursor() self.db_manager.commit_changes() # Close app on closing main form def closeEvent(self, event): exit()