1
0
Files
computational-math/Л3-В6/Программа/src/window.py
2024-03-25 14:14:12 +03:00

435 lines
12 KiB
Python

import sys
from PyQt6.QtWidgets import (
QMessageBox,
QMainWindow,
QLabel,
QComboBox,
QPushButton,
QFormLayout,
QWidget,
QLineEdit,
QHBoxLayout,
QLayout,
QApplication,
)
from PyQt6.QtCore import Qt
import numpy as np
import task
import algorithm
import solution
class DisplayField(QWidget):
label_text: str
def __init__(self, parent, label_text="", text=""):
super().__init__(parent)
self._layout = QHBoxLayout(self)
self._layout.setContentsMargins(0, 0, 0, 0)
self.label_text = label_text
self.text = QLabel(f"{self.label_text}{text}")
self.text.setAlignment(Qt.AlignmentFlag.AlignLeft)
self._layout.addWidget(self.text)
def set_visible(self, visible):
self.text.setVisible(visible)
def set_text(self, text):
self.text.setText(f"{self.label_text}{text}")
class InputField(QWidget):
value: str
change_func: callable = None
def __init__(
self,
parent,
label_text="",
placeholder_text="",
lineedit_text="",
label_position="left",
):
super().__init__(parent)
self._layout = QHBoxLayout(self)
self._layout.setContentsMargins(0, 0, 0, 0)
self.label = QLabel(label_text)
self.lineedit = QLineEdit(lineedit_text)
self.lineedit.setPlaceholderText(placeholder_text)
self.lineedit.setMinimumWidth(50)
if label_position == "left":
self._layout.addWidget(self.label)
self._layout.addWidget(self.lineedit)
elif label_position == "right":
self._layout.addWidget(self.lineedit)
self._layout.addWidget(self.label)
elif label_position == "none":
self._layout.addWidget(self.lineedit)
self.value = self.get_value()
self.lineedit.textChanged.connect(self._on_change)
def get_value(self):
return self.lineedit.text()
def set_value(self, value):
self.lineedit.setText(value)
def on_change(self, func):
self.change_func = func
def _on_change(self):
if self.change_func:
self.change_func(self)
self.value = self.get_value()
def on_input_float_number(widget: InputField):
try:
str_value = widget.get_value()
if not (str_value == "" or str_value == "-"):
widget.value = str(float(str_value))
except:
widget.set_value(str(widget.value))
def on_input_int_number(widget: InputField):
try:
str_value = widget.get_value()
if not (str_value == "" or str_value == "-"):
widget.value = str(int(str_value))
except:
widget.set_value(str(widget.value))
class MatrixRowWidget(QWidget):
x_inputs: list[InputField]
b_input: InputField
def __init__(self, parent, i: int, n: int):
super().__init__(parent)
self._layout = QHBoxLayout()
self._layout.setContentsMargins(0, 0, 0, 0)
self.x_inputs = []
for j in range(n):
inp = InputField(self, f"x{j + 1}", "", "0.0", "right")
inp.lineedit.setAlignment(Qt.AlignmentFlag.AlignRight)
self.x_inputs.append(inp)
self._layout.addWidget(inp)
if j != n - 1:
self._layout.addWidget(QLabel(" + "))
self._layout.addWidget(QLabel(" = "))
self.b_input = InputField(self, "", "", "0.0", "none")
self.b_input.lineedit.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._layout.addWidget(self.b_input)
self.setLayout(self._layout)
def get_value_x(self, i: int):
return self.x_inputs[i].get_value()
def get_value_b(self):
return self.b_input.get_value()
def on_change(self, func):
for inp in self.x_inputs:
inp.on_change(func)
self.b_input.on_change(func)
class MatrixWidget(QWidget):
rows: list[MatrixRowWidget]
_layout: QFormLayout
on_change_func = None
def __init__(self, parent, n: int):
super().__init__(parent)
self._initialize()
self._create(n)
def _initialize(self):
self.rows = []
self._layout = QFormLayout()
self._layout.setContentsMargins(0, 0, 0, 0)
def _create(self, n: int):
for i in range(n):
self.rows.append(MatrixRowWidget(self, i, n))
for row in self.rows:
self._layout.addRow(row)
if self.on_change_func:
row.on_change(self.on_change_func)
self.setLayout(self._layout)
def recreate(self, n: int):
for row in self.rows:
row.deleteLater()
self.rows = []
self._create(n)
def set_value_x(self, i: int, j: int, value: float):
self.rows[i].x_inputs[j].set_value(value)
def set_value_b(self, i: int, value: float):
self.rows[i].b_input.set_value(value)
def get_value_x(self, i: int, j: int):
return self.rows[i].get_value_x(j)
def get_value_b(self, i: int):
return self.rows[i].get_value_b()
def on_change(self, func):
self.on_change_func = func
for row in self.rows:
if self.on_change_func:
row.on_change(self.on_change_func)
class MainWindow(QMainWindow):
initialized = False
matrix_a: np.matrix
matrix_b: np.matrix
eps_input: InputField
n_input: InputField
det_display: DisplayField
cond_display: DisplayField
def __init__(self):
super().__init__()
self.setupUI()
self.initialized = True
self.update_stats()
def setupUI(self):
self.setWindowTitle("Лабораторная работа №3")
self.resize(700, 200)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
layout = QFormLayout()
label1 = QLabel("Вариант 6", self)
layout.addWidget(label1)
inputs_row = QWidget()
inputs_row_layout = QHBoxLayout()
self.n_input = InputField(self, " n: ", "число строк", str(4), "left")
self.n_input.on_change(lambda w: self.on_n_input_changed(w))
inputs_row_layout.addWidget(self.n_input)
self.eps_input = InputField(self, " eps: ", "точность", str(0.001), "left")
self.eps_input.on_change(lambda w: self.on_eps_input_changed(w))
inputs_row_layout.addWidget(self.eps_input)
inputs_row.setLayout(inputs_row_layout)
layout.addWidget(inputs_row)
buttons_row = QWidget()
buttons_row_layout = QHBoxLayout()
buttons_row_layout.setContentsMargins(0, 0, 0, 0)
self.reset_button = QPushButton("Сбросить", self)
self.reset_button.clicked.connect(self.on_reset_button_clicked)
buttons_row_layout.addWidget(self.reset_button)
self.paste_button = QPushButton("Вставить", self)
self.paste_button.clicked.connect(self.on_paste_button_clicked)
buttons_row_layout.addWidget(self.paste_button)
self.random_button = QPushButton("Случайная", self)
self.random_button.clicked.connect(self.on_random_button_clicked)
buttons_row_layout.addWidget(self.random_button)
buttons_row.setLayout(buttons_row_layout)
layout.addWidget(buttons_row)
self.matrix = MatrixWidget(self, 4)
self.matrix.on_change(lambda w: self.on_matrix_changed(w))
self.set_matrix(task.A(), task.B())
layout.addWidget(self.matrix)
self.det_display = DisplayField(self, "Определитель: ", "")
layout.addWidget(self.det_display)
self.cond_display = DisplayField(self, "Число обусловленности: ", "")
layout.addWidget(self.cond_display)
find_solution_button = QPushButton("Решить", self)
find_solution_button.clicked.connect(self.on_find_solution_clicked)
layout.addWidget(find_solution_button)
self.solution_display = DisplayField(self, "Решение: ", "")
layout.addWidget(self.solution_display)
self.solution_display.set_visible(False)
self.central_widget.setLayout(layout)
def on_eps_input_changed(self, widget: InputField):
on_input_float_number(widget)
eps = widget.get_value()
try:
if float(eps) < 0:
eps = 0
self.eps_input.set_value(eps)
except:
return
def on_random_button_clicked(self):
n = int(self.n_input.get_value())
self.matrix_a = np.round(np.random.rand(n, n) * 10, 2)
self.matrix_b = np.round(np.random.rand(n, 1) * 10, 2)
self.set_matrix(self.matrix_a, self.matrix_b)
def on_find_solution_clicked(self):
try:
print(f"Numpy solution: {np.linalg.solve(self.matrix_a, self.matrix_b)}")
except:
print("Numpy could not solve the system")
try:
eps = float(self.eps_input.get_value())
except:
eps = 0
x = solution.iterative_method(self.matrix_a, self.matrix_b, eps)
print(f"Iterative solution: {x}")
if x is None:
self.solution_display.set_visible(True)
self.solution_display.set_text("Нет решения")
else:
self.solution_display.set_visible(True)
solution_text = "\n"
for i in range(x.shape[0]):
solution_text += f"x{i + 1} = {x[i, 0]:.5f}"
if i < x.shape[0] - 1:
solution_text += "\n"
self.solution_display.set_text(solution_text)
def on_paste_button_clicked(self):
text = QApplication.clipboard().text()
try:
input_np = np.fromstring(text, dtype=np.float64, sep=" ")
n = 0.5 * (np.sqrt(4.0 * input_np.shape[0] + 1.0) - 1.0)
if n % 1.0 != 0.0:
return
n = int(n)
input_np = np.reshape(input_np, (n, n + 1))
self.n_input.set_value(str(n))
self.matrix_a = np.copy(input_np[:n, :n])
self.matrix_b = np.copy(input_np[:n, n:])
self.set_matrix(self.matrix_a, self.matrix_b)
except Exception as e:
print(e)
return
def on_n_input_changed(self, widget: InputField):
MAX_N = 8
on_input_int_number(widget)
try:
n = int(widget.get_value())
if n < 1:
widget.set_value(str(1))
return
if n > MAX_N:
widget.set_value(str(MAX_N))
return
self.matrix.recreate(int(self.n_input.get_value()))
self.input_matrix_from_widget()
self.update_stats()
except:
return
def on_reset_button_clicked(self):
self.n_input.set_value(str(4))
self.eps_input.set_value(str(0.001))
self.set_matrix(task.A(), task.B())
def on_matrix_changed(self, widget: InputField):
on_input_float_number(widget)
try:
self.input_matrix_from_widget()
self.update_stats()
except:
return
def input_matrix_from_widget(self):
try:
self.matrix_a = np.matrix(
[
[
self.matrix.get_value_x(i, j)
for j in range(self.matrix.rows[0].x_inputs.__len__())
]
for i in range(self.matrix.rows.__len__())
],
dtype=np.float64,
)
self.matrix_b = np.matrix(
[
[self.matrix.get_value_b(i)]
for i in range(self.matrix.rows.__len__())
],
dtype=np.float64,
)
except:
return
def update_stats(self):
if not self.initialized:
return
self.solution_display.set_visible(False)
try:
self.det_display.set_text(f"{algorithm.det(self.matrix_a):.5f}")
except:
self.det_display.set_text("Невозможно вычислить")
try:
self.cond_display.set_text(f"{algorithm.cond(self.matrix_a):.5f}")
except:
self.cond_display.set_text("Невозможно вычислить")
def set_matrix(self, A: np.matrix, B: np.matrix):
for i in range(A.shape[0]):
for j in range(A.shape[1]):
self.matrix.set_value_x(i, j, str(A[i, j]))
for i in range(B.shape[0]):
self.matrix.set_value_b(i, str(B[i, 0]))
self.update_stats()