DiscreteMathematics/Lab_3/window_2.py

228 lines
8.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from PyQt5 import QtCore, QtWidgets
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import random
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from prettytable import PrettyTable
def is_square(matrix):
return all(len(row) == len(matrix) for row in matrix)
def validate_values(matrix):
return all(all(("1" == element) or ("0" == element) for element in row) for row in matrix)
class Window(QtWidgets.QWidget):
def __init__(self):
super(QtWidgets.QWidget, self).__init__()
self.setWindowTitle("Вікно 2")
self.grid_layout = QtWidgets.QGridLayout(self)
self.adjacency_input_label = QtWidgets.QLabel(self)
self.adjacency_input_label.setText("Введіть матрицю суміжності графа")
self.grid_layout.addWidget(self.adjacency_input_label, 0, 0, 1, 1)
self.adjacency_input = QtWidgets.QTextEdit(self)
self.grid_layout.addWidget(self.adjacency_input, 1, 0, 2, 1)
self.adjacency_output_label = QtWidgets.QLabel(self)
self.adjacency_output_label.setText("Матриця суміжності")
self.grid_layout.addWidget(self.adjacency_output_label, 0, 1, 1, 1)
self.adjacency_output = QtWidgets.QLabel(self)
self.grid_layout.addWidget(self.adjacency_output, 1, 1, 1, 1)
self.incidence_output_label = QtWidgets.QLabel(self)
self.incidence_output_label.setText("Матриця інцидентності")
self.grid_layout.addWidget(self.incidence_output_label, 0, 2, 1, 1)
self.incidence_output = QtWidgets.QLabel(self)
self.grid_layout.addWidget(self.incidence_output, 1, 2, 1, 1)
self.edges_dictionary_output_label = QtWidgets.QLabel(self)
self.edges_dictionary_output_label.setText("Словник граней")
self.grid_layout.addWidget(self.edges_dictionary_output_label, 0, 3, 1, 1)
self.edges_dictionary_output = QtWidgets.QLabel(self)
self.grid_layout.addWidget(self.edges_dictionary_output, 1, 3, 1, 1)
self.canvas_grid = QtWidgets.QGridLayout()
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.canvas_grid.addWidget(self.canvas, 0, 0, 1, 1)
self.grid_layout.addLayout(self.canvas_grid, 2, 1, 1, 3)
self.build_graph_button = QtWidgets.QPushButton(self)
self.build_graph_button.setText("Побудувати граф")
self.grid_layout.addWidget(self.build_graph_button, 3, 0, 1, 2)
self.open_file_button = QtWidgets.QPushButton(self)
self.open_file_button.setText("Відкрити файл")
self.grid_layout.addWidget(self.open_file_button, 3, 2, 1, 2)
QtCore.QMetaObject.connectSlotsByName(self)
self.set_functions()
def parse_matrix(self) -> (np.ndarray | None, str | None):
text_matrix = list(map(lambda x: x.split(" "), self.adjacency_input.toPlainText().splitlines()))
if not is_square(text_matrix):
return None, "square_error"
elif not validate_values(text_matrix):
return None, "value_error"
else:
return np.array(list([list(map(int, row)) for row in text_matrix])), None
def build_graphs(self):
adjacency_matrix, error = self.parse_matrix()
if adjacency_matrix is None and error == "square_error":
self.show_error(
"Матриця суміжності має бути квадратною. Перевірте чи кожен елемент рядка відділений пробілом і чи "
"дорівнює кількість рядків кількості стовпців."
)
elif adjacency_matrix is None and error == "value_error":
self.show_error(
"Матриця суміжності мусить складатись лише з нулів і одиниць розділених пробілом. Перевірте ваш ввід."
)
else:
adjacency_graph = self.create_adjacency_graph(adjacency_matrix)
self.display_adjacency_matrix(adjacency_graph, self.adjacency_output)
incidence_nodes, incidence_edges, incidence_matrix = self.create_incidence_matrix(adjacency_graph)
self.display_incidence_matrix(incidence_nodes, incidence_edges, incidence_matrix, self.incidence_output)
incidence_graph = self.create_incidence_graph(incidence_nodes, incidence_edges)
self.draw_graph(adjacency_graph, incidence_graph)
self.display_edge_dictionary(adjacency_graph, self.edges_dictionary_output)
@staticmethod
def show_error(error_text: str):
error_message = QtWidgets.QMessageBox()
error_message.setIcon(QtWidgets.QMessageBox.Critical)
error_message.setText("Помилка вводу")
error_message.setInformativeText(error_text)
error_message.setWindowTitle("Помилка")
error_message.exec_()
@staticmethod
def create_adjacency_graph(adjacency_matrix: np.ndarray) -> nx.DiGraph:
graph = nx.DiGraph()
nodes = [f"X{i}" for i in range(len(adjacency_matrix))]
edge_indices = np.argwhere(adjacency_matrix)
edges = [(nodes[edge_index[0]], nodes[edge_index[1]]) for edge_index in edge_indices]
graph.add_nodes_from(nodes)
graph.add_edges_from(edges)
return graph
@staticmethod
def create_incidence_graph(nodes: list, edges: list) -> nx.DiGraph:
graph = nx.DiGraph()
graph.add_nodes_from(nodes)
graph.add_edges_from(edges)
return graph
def draw_graph(self, adjacency_graph: nx.DiGraph, incidence_graph: nx.DiGraph):
random.seed(0)
np.random.seed(0)
self.figure.clf()
pos = nx.planar_layout(adjacency_graph, scale=0.5)
nx.draw(
adjacency_graph, pos=pos, with_labels=True, node_color="blueviolet", node_size=700, font_color="white",
font_size=14, font_family="Liberation Mono", font_weight="bold", width=2
)
pos = nx.planar_layout(incidence_graph, scale=0.5)
for i in pos:
pos[i][0] += 2
nx.draw(
adjacency_graph, pos=pos, with_labels=True, node_color="blueviolet", node_size=700, font_color="white",
font_size=14, font_family="Liberation Mono", font_weight="bold", width=2
)
self.canvas.draw_idle()
@staticmethod
def display_adjacency_matrix(graph: nx.DiGraph, label: QtWidgets.QLabel):
matrix = nx.adjacency_matrix(graph).todense().tolist()
table = PrettyTable()
nodes = [f"X{i}" for i in range(len(matrix))]
table.field_names = [" "] + nodes
for i in range(len(matrix)):
table.add_row([nodes[i]] + matrix[i])
label.setText(table.get_string())
@staticmethod
def display_incidence_matrix(nodes: list, edges: list, incidence_matrix: list, label: QtWidgets.QLabel):
table = PrettyTable()
edge_names = [f"E{i}" for i in range(len(edges))]
table.field_names = [" "] + edge_names
for i in range(len(incidence_matrix)):
table.add_row([nodes[i]] + incidence_matrix[i])
label.setText(table.get_string())
@staticmethod
def create_incidence_matrix(graph: nx.DiGraph) -> (list, list, list):
nodes = list(graph.nodes)
edges = list(graph.edges)
edge_names = [f"E{i}" for i in range(len(edges))]
matrix = [["0" for j in range(len(edges))] for i in range(len(nodes))]
matrix = [
["1" if nodes[i] == edges[j][0] else matrix[i][j] for j in range(len(matrix[i]))]
for i in range(len(matrix))]
matrix = [
["-1" if nodes[i] == edges[j][1] else matrix[i][j] for j in range(len(matrix[i]))]
for i in range(len(matrix))]
matrix = [
["±1" if (nodes[i] == edges[j][0] and nodes[i] == edges[j][1])
else matrix[i][j] for j in range(len(matrix[i]))]
for i in range(len(matrix))]
return nodes, edges, matrix
@staticmethod
def display_edge_dictionary(graph: nx.DiGraph, label: QtWidgets.QLabel):
edge_dictionary = ""
edges = list(graph.edges)
for i, edge in enumerate(edges):
edge_dictionary += f"E{i} = ({edge[0]} -> {edge[1]})\n"
label.setText(edge_dictionary)
def browse_files(self):
file_name = QtWidgets.QFileDialog.getOpenFileName(self, "Відкрити файл", "~/", "Текстові файли (*.txt)")
with open(file_name[0]) as f:
text_read = f.read()
self.adjacency_input.setText(text_read)
def set_functions(self):
self.build_graph_button.clicked.connect(lambda: self.build_graphs())
self.open_file_button.clicked.connect(lambda: self.browse_files())