from PyQt5 import QtCore, QtWidgets import sys import networkx as nx import matplotlib.pyplot as plt import matplotlib.colors as mcolours import numpy as np from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas class Window(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Вікно 1") central_widget = QtWidgets.QWidget(self) self.grid_layout = QtWidgets.QGridLayout(central_widget) self.id_grid_layout = QtWidgets.QGridLayout() self.full_name = QtWidgets.QLabel(central_widget) self.full_name.setText("П. І. Б:") self.full_name.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.id_grid_layout.addWidget(self.full_name, 0, 0, 1, 1) self.full_name_answer = QtWidgets.QLabel(central_widget) self.full_name_answer.setText("Швед Андрій Дмитрович") self.id_grid_layout.addWidget(self.full_name_answer, 0, 1, 1, 1) self.group = QtWidgets.QLabel(central_widget) self.group.setText("Група:") self.group.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.id_grid_layout.addWidget(self.group, 1, 0, 1, 1) self.group_answer = QtWidgets.QLabel(central_widget) self.group_answer.setText("ІО-23") self.id_grid_layout.addWidget(self.group_answer, 1, 1, 1, 1) self.list_number = QtWidgets.QLabel(central_widget) self.list_number.setText("Номер в списку:") self.list_number.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.id_grid_layout.addWidget(self.list_number, 2, 0, 1, 1) self.list_number_answer = QtWidgets.QLabel(central_widget) self.list_number_answer.setText("30") self.id_grid_layout.addWidget(self.list_number_answer, 2, 1, 1, 1) self.task_number = QtWidgets.QLabel(central_widget) self.task_number.setText("Номер завдання:") self.task_number.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.id_grid_layout.addWidget(self.task_number, 3, 0, 1, 1) self.task_number_answer = QtWidgets.QLabel(central_widget) self.task_number_answer.setText("1") self.id_grid_layout.addWidget(self.task_number_answer, 3, 1, 1, 1) self.grid_layout.addLayout(self.id_grid_layout, 0, 0, 1, 1) self.graph_grid_layout = QtWidgets.QGridLayout() self.adjacency_input_label = QtWidgets.QLabel(self) self.adjacency_input_label.setText("Введіть матрицю суміжності графа") self.graph_grid_layout.addWidget(self.adjacency_input_label, 0, 0, 1, 1) self.adjacency_input = QtWidgets.QTextEdit(self) self.graph_grid_layout.addWidget(self.adjacency_input, 1, 0, 2, 1) self.graph_label = QtWidgets.QLabel(self) self.graph_label.setText("Розфарбований граф") self.graph_grid_layout.addWidget(self.graph_label, 0, 1, 1, 1) self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.graph_grid_layout.addWidget(self.canvas, 1, 1, 1, 1) self.grid_layout.addLayout(self.graph_grid_layout, 1, 0, 1, 1) self.buttons_grid_layout = QtWidgets.QGridLayout() self.build_graph_button = QtWidgets.QPushButton(self) self.build_graph_button.setText("Побудувати граф") self.buttons_grid_layout.addWidget(self.build_graph_button, 0, 0, 1, 1) self.open_file_button = QtWidgets.QPushButton(self) self.open_file_button.setText("Відкрити файл") self.buttons_grid_layout.addWidget(self.open_file_button, 0, 1, 1, 1) self.grid_layout.addLayout(self.buttons_grid_layout, 2, 0, 1, 1) self.setCentralWidget(central_widget) self.menubar = QtWidgets.QMenuBar(self) self.menubar.setGeometry(QtCore.QRect(0, 0, 362, 22)) self.menubar.setDefaultUp(False) self.menu_Windows = QtWidgets.QMenu(self.menubar) self.menu_Windows.setTitle("Вікна") self.setMenuBar(self.menubar) self.exit_action = QtWidgets.QAction(self) self.exit_action.setText("Вихід") self.menu_Windows.addAction(self.exit_action) self.menubar.addAction(self.menu_Windows.menuAction()) self.set_menu() self.set_functions() QtCore.QMetaObject.connectSlotsByName(self) def set_menu(self): self.exit_action.triggered.connect(lambda: sys.exit()) @staticmethod def is_square(matrix: list) -> bool: return all(len(row) == len(matrix) for row in matrix) @staticmethod def validate_values(matrix: list) -> bool: return all(all(("1" == element) or ("0" == element) for element in row) for row in matrix) def parse_matrix(self) -> (np.ndarray | None, str | None): text_matrix = list(map(lambda x: x.split(" "), self.adjacency_input.toPlainText().splitlines())) if not self.is_square(text_matrix): return None, "square_error" elif not self.validate_values(text_matrix): return None, "value_error" elif not text_matrix: return None, "empty_matrix" 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( "Матриця суміжності мусить складатись лише з нулів і одиниць розділених пробілом. Перевірте ваш ввід." ) elif adjacency_matrix is None and error == "empty_matrix": self.show_error( "Матриця суміжності не може бути пустою." ) elif type(adjacency_matrix) is np.ndarray and error is None: adjacency_graph = self.create_adjacency_graph(adjacency_matrix) colours = self.brute_force_colour(adjacency_graph) self.draw_graph(adjacency_graph, colours) @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.Graph: graph = nx.Graph() nodes = [f"{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 def draw_graph(self, graph: nx.Graph, colour_map: list): self.figure.clf() nx.draw( graph, with_labels=True, node_size=700, font_size=14, font_family="Ubuntu Mono", node_color=colour_map, edgecolors="black", font_weight="bold", width=2 ) plt.margins(0.2) self.canvas.draw_idle() @staticmethod def previous_neighbors(graph: nx.Graph, node: str): neighbours = list(graph.neighbors(node)) nodes = list(graph.nodes) previous = nodes[:nodes.index(node)] res = [n for n in previous if n in neighbours] return res def brute_force_colour(self, graph: nx.Graph) -> list: coloured_nodes = dict() i = 0 for node in graph: previous_colours = [coloured_nodes[f'{n}'] for n in self.previous_neighbors(graph, node)] nodes_difference = sorted(list(set(coloured_nodes.values()) - set(previous_colours))) if (not coloured_nodes) or (not nodes_difference): coloured_nodes[f"{node}"] = i i += 1 else: coloured_nodes[f"{node}"] = nodes_difference[0] colour_map = [list(mcolours.CSS4_COLORS.keys())[i] for i in coloured_nodes.values()] return colour_map def open_file(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.open_file())