From c4cc4f6eaf8cdc4983b98f9d67e480ec595d8cf1 Mon Sep 17 00:00:00 2001 From: 6ixld Date: Sun, 26 Oct 2025 16:11:42 +0300 Subject: [PATCH 01/10] Add initial project files: app.py, annotation.docx, requirements.txt, sets_logic.py, script.js, style.css, and index.html --- app.py | 0 docs/annotation.docx | 0 requirements.txt | 0 sets_logic.py | 0 static/script.js | 0 static/style.css | 0 templates/index.html | 0 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app.py create mode 100644 docs/annotation.docx create mode 100644 requirements.txt create mode 100644 sets_logic.py create mode 100644 static/script.js create mode 100644 static/style.css create mode 100644 templates/index.html diff --git a/app.py b/app.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/annotation.docx b/docs/annotation.docx new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/sets_logic.py b/sets_logic.py new file mode 100644 index 0000000..e69de29 diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..e69de29 diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..e69de29 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..e69de29 From f9dbea9a920aef6a7ef7611073591548baafe336 Mon Sep 17 00:00:00 2001 From: 6ixld Date: Sun, 26 Oct 2025 17:59:35 +0300 Subject: [PATCH 02/10] Implement core functionality for set operations and Venn diagram visualization --- app.py | 80 ++++++++++++++++++++++++++++++++++++++++++++ sets_logic.py | 14 ++++++++ static/script.js | 15 +++++++++ static/style.css | 18 ++++++++++ static/venn.png | 0 templates/index.html | 39 +++++++++++++++++++++ venn_visual.py | 37 ++++++++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 static/venn.png create mode 100644 venn_visual.py diff --git a/app.py b/app.py index e69de29..d4252f7 100644 --- a/app.py +++ b/app.py @@ -0,0 +1,80 @@ +from flask import Flask, render_template, request, jsonify +from venn_visual import plot_venn +from sets_logic import * + +app = Flask(__name__) + +@app.route('/', methods=['GET', 'POST']) +def index(): + img = None + result_text = "" + if request.method == 'POST': + # получаем множества из формы + raw_a = request.form.get('set_a', '') + raw_b = request.form.get('set_b', '') + operation = request.form.get('operation', 'union') # получаем выбранную операцию + + # преобразование в Python set + a = set(raw_a.replace(' ', '').split(',')) if raw_a else set() + b = set(raw_b.replace(' ', '').split(',')) if raw_b else set() + + # Выбор операции + if operation == 'union': + result = union(a, b) + operation_name = "объединения" + elif operation == 'intersection': + result = intersection(a, b) + operation_name = "пересечения" + elif operation == 'difference': + result = difference(a, b) + operation_name = "разности A\\B" + elif operation == 'sym_diff': + result = sym_diff(a, b) + operation_name = "симметрической разности" + elif operation == 'cartesian': + result = cartesian(a, b) + operation_name = "декартового произведения" + + # Форматирование результата + if operation == 'cartesian': + result_text = f"Результат {operation_name}: {', '.join(str(x) for x in result)}" + else: + result_text = f"Результат {operation_name}: {', '.join(str(x) for x in sorted(result))}" + + # генерируем диаграмму только для операций с 2-мя множествами + if a and b: + img = plot_venn(a, b) + + return render_template('index.html', img=img, result_text=result_text) + +@app.route('/process', methods=['POST']) +def process(): + data = request.get_json() + a = set(data.get('setA', '').replace(' ', '').split(',')) if data.get('setA') else set() + b = set(data.get('setB', '').replace(' ', '').split(',')) if data.get('setB') else set() + operation = data.get('operation', 'union') + + # Выбор операции + if operation == 'union': + result = union(a, b) + elif operation == 'intersection': + result = intersection(a, b) + elif operation == 'difference': + result = difference(a, b) + elif operation == 'sym_diff': + result = sym_diff(a, b) + elif operation == 'cartesian': + result = cartesian(a, b) + else: + result = set() + + # Если результат — множество кортежей (для декартового произведения), конвертируем в список строк + if operation == 'cartesian': + result_list = [str(t) for t in result] + else: + result_list = list(result) + + return jsonify({'result': result_list}) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/sets_logic.py b/sets_logic.py index e69de29..3815a20 100644 --- a/sets_logic.py +++ b/sets_logic.py @@ -0,0 +1,14 @@ +def union(a, b): + return a | b + +def intersection(a, b): + return a & b + +def difference(a, b): + return a - b + +def sym_diff(a, b): + return a ^ b + +def cartesian(a, b): + return {(x, y) for x in a for y in b} diff --git a/static/script.js b/static/script.js index e69de29..53b7642 100644 --- a/static/script.js +++ b/static/script.js @@ -0,0 +1,15 @@ +function sendData() { + const setA = document.getElementById('setA').value.split(',').map(x => x.trim()); + const setB = document.getElementById('setB').value.split(',').map(x => x.trim()); + const operation = document.getElementById('operation').value; + + fetch('/process', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ setA, setB, operation }) + }) + .then(res => res.json()) + .then(data => { + document.getElementById('result').innerText = `{ ${data.result.join(', ')} }`; + }); +} diff --git a/static/style.css b/static/style.css index e69de29..55fa284 100644 --- a/static/style.css +++ b/static/style.css @@ -0,0 +1,18 @@ +body { + font-family: Arial, sans-serif; + max-width: 600px; + margin: 50px auto; + padding: 20px; + background-color: #f9f9f9; +} + +input, select, button { + display: block; + margin: 10px 0; + padding: 8px; + width: 100%; +} + +button { + cursor: pointer; +} diff --git a/static/venn.png b/static/venn.png new file mode 100644 index 0000000..e69de29 diff --git a/templates/index.html b/templates/index.html index e69de29..26b5b4c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -0,0 +1,39 @@ + + + + + Визуализация множеств + + + + +

Визуализация операций над множествами

+ +
+ + + + + + + + + +
+ + {% if result_text %} +

{{ result_text }}

+ {% endif %} + + {% if img %} +

Диаграмма Венна:

+ Диаграмма Венна + {% endif %} + + diff --git a/venn_visual.py b/venn_visual.py new file mode 100644 index 0000000..a0ac3b4 --- /dev/null +++ b/venn_visual.py @@ -0,0 +1,37 @@ +# venn_visual.py +import matplotlib +matplotlib.use('Agg') # <- критично! Без оконного backend +import matplotlib.pyplot as plt +from matplotlib_venn import venn2 +import io +import base64 + +def plot_venn(a, b, set_labels=('A', 'B')): + """ + Создаёт диаграмму Венна для двух множеств и возвращает изображение в base64 для вставки в HTML. + + :param a: множество A (set) + :param b: множество B (set) + :param set_labels: подписи множеств + :return: base64 строка с изображением + """ + # Создаём фигуру + plt.figure(figsize=(5,5)) + + # Строим диаграмму Венна + v = venn2([a, b], set_labels=set_labels) + + # Подписи элементов каждого сектора (если не пусто) + for idx, subset in enumerate(v.subset_labels): + if subset: + subset.set_text(subset.get_text()) + + # Сохраняем в буфер PNG + buf = io.BytesIO() + plt.savefig(buf, format='png', bbox_inches='tight') + plt.close() # закрываем фигуру, чтобы не создавались окна + buf.seek(0) + + # Конвертируем в base64 для вставки в HTML + img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8') + return img_base64 From 2ee37cf1a4a2551221948281b587e9fef4fd1297 Mon Sep 17 00:00:00 2001 From: Steve Whois Date: Sun, 26 Oct 2025 22:34:45 +0300 Subject: [PATCH 03/10] Add files via upload --- app.py | 80 +++++++++++++++++++++++++++++ deepseek_python_20251026_489bbe.py | 14 ++++++ requirements.txt | 0 sets_logic.py | 14 ++++++ venn_visual.py | 81 ++++++++++++++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 app.py create mode 100644 deepseek_python_20251026_489bbe.py create mode 100644 requirements.txt create mode 100644 sets_logic.py create mode 100644 venn_visual.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..d65a641 --- /dev/null +++ b/app.py @@ -0,0 +1,80 @@ +from flask import Flask, render_template, request, jsonify +from venn_visual import plot_venn # Убрал лишний импорт +from sets_logic import * + +app = Flask(__name__) + +@app.route('/', methods=['GET', 'POST']) +def index(): + img = None + result_text = "" + if request.method == 'POST': + # получаем множества из формы + raw_a = request.form.get('set_a', '') + raw_b = request.form.get('set_b', '') + operation = request.form.get('operation', 'union') # получаем выбранную операцию + + # преобразование в Python set + a = set(raw_a.replace(' ', '').split(',')) if raw_a else set() + b = set(raw_b.replace(' ', '').split(',')) if raw_b else set() + + # Выбор операции + if operation == 'union': + result = union(a, b) + operation_name = "объединения" + elif operation == 'intersection': + result = intersection(a, b) + operation_name = "пересечения" + elif operation == 'difference': + result = difference(a, b) + operation_name = "разности A\\B" + elif operation == 'sym_diff': + result = sym_diff(a, b) + operation_name = "симметрической разности" + elif operation == 'cartesian': + result = cartesian(a, b) + operation_name = "декартового произведения" + + # Форматирование результата + if operation == 'cartesian': + result_text = f"Результат {operation_name}: {', '.join(str(x) for x in result)}" + else: + result_text = f"Результат {operation_name}: {', '.join(str(x) for x in sorted(result))}" + + # генерируем диаграмму только для операций с 2-мя множествами + if a and b: + img = plot_venn(a, b) + + return render_template('index.html', img=img, result_text=result_text) + +@app.route('/process', methods=['POST']) +def process(): + data = request.get_json() + a = set(data.get('setA', '').replace(' ', '').split(',')) if data.get('setA') else set() + b = set(data.get('setB', '').replace(' ', '').split(',')) if data.get('setB') else set() + operation = data.get('operation', 'union') + + # Выбор операции + if operation == 'union': + result = union(a, b) + elif operation == 'intersection': + result = intersection(a, b) + elif operation == 'difference': + result = difference(a, b) + elif operation == 'sym_diff': + result = sym_diff(a, b) + elif operation == 'cartesian': + result = cartesian(a, b) + else: + result = set() + + # Если результат — множество кортежей (для декартового произведения), конвертируем в список строк + if operation == 'cartesian': + result_list = [str(t) for t in result] + else: + result_list = list(result) + + return jsonify({'result': result_list}) + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/deepseek_python_20251026_489bbe.py b/deepseek_python_20251026_489bbe.py new file mode 100644 index 0000000..4018f25 --- /dev/null +++ b/deepseek_python_20251026_489bbe.py @@ -0,0 +1,14 @@ +def union(a, b): + return a | b + +def intersection(a, b): + return a & b + +def difference(a, b): + return a - b + +def sym_diff(a, b): + return a ^ b + +def cartesian(a, b): + return {(x, y) for x in a for y in b} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/sets_logic.py b/sets_logic.py new file mode 100644 index 0000000..3815a20 --- /dev/null +++ b/sets_logic.py @@ -0,0 +1,14 @@ +def union(a, b): + return a | b + +def intersection(a, b): + return a & b + +def difference(a, b): + return a - b + +def sym_diff(a, b): + return a ^ b + +def cartesian(a, b): + return {(x, y) for x in a for y in b} diff --git a/venn_visual.py b/venn_visual.py new file mode 100644 index 0000000..c2b4719 --- /dev/null +++ b/venn_visual.py @@ -0,0 +1,81 @@ +# venn_visual.py +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +from matplotlib_venn import venn2 +import io +import base64 + +def plot_venn(a, b, set_labels=('A', 'B')): + """ + Создаёт красивую диаграмму Венна для двух множеств с отображением элементов. + """ + # Создаём фигуру с современным дизайном + plt.figure(figsize=(8, 8), facecolor='#f8f9fa') + ax = plt.gca() + ax.set_facecolor('#f8f9fa') + + # Цветовая схема + colors = ['#ff6b6b', '#4ecdc4'] + text_color = '#2d3436' + + # Строим диаграмму Венна + v = venn2([a, b], set_labels=set_labels, + set_colors=colors, alpha=0.8) + + # Очищаем стандартные подписи с количествами и добавляем элементы + for label in v.subset_labels: + if label: + label.set_text('') + + # Добавляем элементы множества A только (левая часть) + if a - b: + elements_a = sorted(a - b) + v.get_label_by_id('10').set_text('\n'.join(map(str, elements_a))) + + # Добавляем элементы пересечения A∩B (центральная часть) + if a & b: + elements_intersect = sorted(a & b) + v.get_label_by_id('11').set_text('\n'.join(map(str, elements_intersect))) + + # Добавляем элементы множества B только (правая часть) + if b - a: + elements_b = sorted(b - a) + v.get_label_by_id('01').set_text('\n'.join(map(str, elements_b))) + + # Настраиваем подписи множеств + for label in v.set_labels: + if label: + label.set_fontsize(16) + label.set_fontweight('bold') + label.set_color(text_color) + + # Настраиваем подписи элементов + for subset_label in v.subset_labels: + if subset_label: + subset_label.set_fontsize(12) + subset_label.set_fontweight('normal') + subset_label.set_color(text_color) + + # Настраиваем внешний вид окружностей + for patch in v.patches: + if patch: + patch.set_edgecolor('#2d3436') + patch.set_linewidth(2) + patch.set_linestyle('-') + + # УБРАЛ ЗАГОЛОВОК ДИАГРАММЫ + # plt.title('Диаграмма Венна', color=text_color, fontsize=18, pad=20, fontweight='bold') + + # Убираем оси для чистого вида + ax.axis('off') + + # Сохраняем с высоким качеством + buf = io.BytesIO() + plt.savefig(buf, format='png', bbox_inches='tight', + facecolor=ax.get_facecolor(), dpi=150, + transparent=False, edgecolor='none') + plt.close() + + img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8') + return img_base64 \ No newline at end of file From d2473bd01436df33fd3643c6eeffdff4ccd26f03 Mon Sep 17 00:00:00 2001 From: 6ixyld <128928774+6ixyld@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:57:17 +0300 Subject: [PATCH 04/10] Delete deepseek_python_20251026_489bbe.py --- deepseek_python_20251026_489bbe.py | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 deepseek_python_20251026_489bbe.py diff --git a/deepseek_python_20251026_489bbe.py b/deepseek_python_20251026_489bbe.py deleted file mode 100644 index 4018f25..0000000 --- a/deepseek_python_20251026_489bbe.py +++ /dev/null @@ -1,14 +0,0 @@ -def union(a, b): - return a | b - -def intersection(a, b): - return a & b - -def difference(a, b): - return a - b - -def sym_diff(a, b): - return a ^ b - -def cartesian(a, b): - return {(x, y) for x in a for y in b} \ No newline at end of file From 628d897b893fdd7b9f4923f37c92bafa0b6e4ac7 Mon Sep 17 00:00:00 2001 From: Steve Whois Date: Tue, 28 Oct 2025 22:08:50 +0300 Subject: [PATCH 05/10] Add files via upload --- script.js | 15 +++++++ style.css | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ venn.png | 0 3 files changed, 144 insertions(+) create mode 100644 script.js create mode 100644 style.css create mode 100644 venn.png diff --git a/script.js b/script.js new file mode 100644 index 0000000..53b7642 --- /dev/null +++ b/script.js @@ -0,0 +1,15 @@ +function sendData() { + const setA = document.getElementById('setA').value.split(',').map(x => x.trim()); + const setB = document.getElementById('setB').value.split(',').map(x => x.trim()); + const operation = document.getElementById('operation').value; + + fetch('/process', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ setA, setB, operation }) + }) + .then(res => res.json()) + .then(data => { + document.getElementById('result').innerText = `{ ${data.result.join(', ')} }`; + }); +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..e5a1c04 --- /dev/null +++ b/style.css @@ -0,0 +1,129 @@ +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + max-width: 900px; + margin: 0 auto; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + color: #333; +} + +.container { + background: white; + border-radius: 20px; + padding: 40px; + box-shadow: 0 20px 40px rgba(0,0,0,0.1); + backdrop-filter: blur(10px); + margin-top: 20px; +} + +h1 { + text-align: center; + color: white; + margin-bottom: 10px; + font-size: 2.8em; + text-shadow: 2px 2px 4px rgba(0,0,0,0.3); + font-weight: 700; +} + +.subtitle { + text-align: center; + color: white; + margin-bottom: 30px; + font-size: 1.2em; + opacity: 0.9; +} + +.form-group { + margin-bottom: 25px; +} + +label { + display: block; + margin-bottom: 10px; + font-weight: 600; + color: #555; + font-size: 1.1em; +} + +input, select, button { + display: block; + margin: 8px 0; + padding: 15px; + width: 100%; + border: 2px solid #e1e8ed; + border-radius: 12px; + font-size: 16px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-sizing: border-box; + font-family: inherit; +} + +input:focus, select:focus { + outline: none; + border-color: #667eea; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15); + transform: translateY(-2px); +} + +button { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + padding: 18px; + font-size: 18px; + font-weight: 600; + cursor: pointer; + margin-top: 25px; + border-radius: 12px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +button:hover { + transform: translateY(-3px); + box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4); +} + +#result { + background: #f8f9fa; /* Нейтральный серый фон */ + color: #2d3436; /* Темно-серый текст */ + border-left: 5px solid #667eea; + padding: 20px; + margin: 30px 0; + border-radius: 12px; + font-size: 1.2em; + font-weight: 500; + box-shadow: 0 5px 15px rgba(0,0,0,0.08); +} + +.venn-container { + text-align: center; + margin: 40px 0; + padding: 30px; + background: #f8f9fa; + border-radius: 15px; + box-shadow: inset 0 2px 10px rgba(0,0,0,0.05); +} + +.venn-container h2 { + color: #2d3436; + margin-bottom: 25px; + font-size: 1.8em; + font-weight: 600; +} + +.venn-container img { + max-width: 100%; + height: auto; + border-radius: 15px; + box-shadow: 0 10px 30px rgba(0,0,0,0.15); + border: 3px solid white; +} + +.operation-info { + text-align: center; + color: #636e72; + font-style: italic; + margin-top: 10px; + font-size: 0.9em; +} \ No newline at end of file diff --git a/venn.png b/venn.png new file mode 100644 index 0000000..e69de29 From d090424148a26b98fb16c54015a497a0a86ce47f Mon Sep 17 00:00:00 2001 From: 6ixyld <128928774+6ixyld@users.noreply.github.com> Date: Tue, 28 Oct 2025 22:21:49 +0300 Subject: [PATCH 06/10] Delete script.js --- script.js | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 script.js diff --git a/script.js b/script.js deleted file mode 100644 index 53b7642..0000000 --- a/script.js +++ /dev/null @@ -1,15 +0,0 @@ -function sendData() { - const setA = document.getElementById('setA').value.split(',').map(x => x.trim()); - const setB = document.getElementById('setB').value.split(',').map(x => x.trim()); - const operation = document.getElementById('operation').value; - - fetch('/process', { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ setA, setB, operation }) - }) - .then(res => res.json()) - .then(data => { - document.getElementById('result').innerText = `{ ${data.result.join(', ')} }`; - }); -} From e341c6e8b6306e287b02d62150ec0ab48011e898 Mon Sep 17 00:00:00 2001 From: 6ixyld <128928774+6ixyld@users.noreply.github.com> Date: Tue, 28 Oct 2025 22:22:02 +0300 Subject: [PATCH 07/10] Delete venn.png --- venn.png | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 venn.png diff --git a/venn.png b/venn.png deleted file mode 100644 index e69de29..0000000 From 80348959cafbf3dc61f6cfdc39d2bfe4f9e2e1a7 Mon Sep 17 00:00:00 2001 From: 6ixyld <128928774+6ixyld@users.noreply.github.com> Date: Tue, 28 Oct 2025 22:22:21 +0300 Subject: [PATCH 08/10] Delete style.css --- style.css | 129 ------------------------------------------------------ 1 file changed, 129 deletions(-) delete mode 100644 style.css diff --git a/style.css b/style.css deleted file mode 100644 index e5a1c04..0000000 --- a/style.css +++ /dev/null @@ -1,129 +0,0 @@ -body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - max-width: 900px; - margin: 0 auto; - padding: 20px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - min-height: 100vh; - color: #333; -} - -.container { - background: white; - border-radius: 20px; - padding: 40px; - box-shadow: 0 20px 40px rgba(0,0,0,0.1); - backdrop-filter: blur(10px); - margin-top: 20px; -} - -h1 { - text-align: center; - color: white; - margin-bottom: 10px; - font-size: 2.8em; - text-shadow: 2px 2px 4px rgba(0,0,0,0.3); - font-weight: 700; -} - -.subtitle { - text-align: center; - color: white; - margin-bottom: 30px; - font-size: 1.2em; - opacity: 0.9; -} - -.form-group { - margin-bottom: 25px; -} - -label { - display: block; - margin-bottom: 10px; - font-weight: 600; - color: #555; - font-size: 1.1em; -} - -input, select, button { - display: block; - margin: 8px 0; - padding: 15px; - width: 100%; - border: 2px solid #e1e8ed; - border-radius: 12px; - font-size: 16px; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-sizing: border-box; - font-family: inherit; -} - -input:focus, select:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15); - transform: translateY(-2px); -} - -button { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - padding: 18px; - font-size: 18px; - font-weight: 600; - cursor: pointer; - margin-top: 25px; - border-radius: 12px; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); -} - -button:hover { - transform: translateY(-3px); - box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4); -} - -#result { - background: #f8f9fa; /* Нейтральный серый фон */ - color: #2d3436; /* Темно-серый текст */ - border-left: 5px solid #667eea; - padding: 20px; - margin: 30px 0; - border-radius: 12px; - font-size: 1.2em; - font-weight: 500; - box-shadow: 0 5px 15px rgba(0,0,0,0.08); -} - -.venn-container { - text-align: center; - margin: 40px 0; - padding: 30px; - background: #f8f9fa; - border-radius: 15px; - box-shadow: inset 0 2px 10px rgba(0,0,0,0.05); -} - -.venn-container h2 { - color: #2d3436; - margin-bottom: 25px; - font-size: 1.8em; - font-weight: 600; -} - -.venn-container img { - max-width: 100%; - height: auto; - border-radius: 15px; - box-shadow: 0 10px 30px rgba(0,0,0,0.15); - border: 3px solid white; -} - -.operation-info { - text-align: center; - color: #636e72; - font-style: italic; - margin-top: 10px; - font-size: 0.9em; -} \ No newline at end of file From eedb5617ab358409692e8c30cd03f65550f70d1e Mon Sep 17 00:00:00 2001 From: Steve Whois Date: Tue, 28 Oct 2025 23:13:22 +0300 Subject: [PATCH 09/10] github pobeda --- app.py | 73 ++++++++++++++---------- static/style.css | 130 ++++++++++++++++++++++++++++++++++++++++--- templates/index.html | 64 +++++++++++++-------- venn_visual.py | 75 ++++++++++++++----------- 4 files changed, 249 insertions(+), 93 deletions(-) diff --git a/app.py b/app.py index d4252f7..e7ed1b8 100644 --- a/app.py +++ b/app.py @@ -1,43 +1,48 @@ from flask import Flask, render_template, request, jsonify -from venn_visual import plot_venn +from venn_visual import plot_venn # Убрал лишний импорт from sets_logic import * app = Flask(__name__) -@app.route('/', methods=['GET', 'POST']) + +@app.route("/", methods=["GET", "POST"]) def index(): img = None result_text = "" - if request.method == 'POST': + if request.method == "POST": # получаем множества из формы - raw_a = request.form.get('set_a', '') - raw_b = request.form.get('set_b', '') - operation = request.form.get('operation', 'union') # получаем выбранную операцию + raw_a = request.form.get("set_a", "") + raw_b = request.form.get("set_b", "") + operation = request.form.get( + "operation", "union" + ) # получаем выбранную операцию # преобразование в Python set - a = set(raw_a.replace(' ', '').split(',')) if raw_a else set() - b = set(raw_b.replace(' ', '').split(',')) if raw_b else set() + a = set(raw_a.replace(" ", "").split(",")) if raw_a else set() + b = set(raw_b.replace(" ", "").split(",")) if raw_b else set() # Выбор операции - if operation == 'union': + if operation == "union": result = union(a, b) operation_name = "объединения" - elif operation == 'intersection': + elif operation == "intersection": result = intersection(a, b) operation_name = "пересечения" - elif operation == 'difference': + elif operation == "difference": result = difference(a, b) operation_name = "разности A\\B" - elif operation == 'sym_diff': + elif operation == "sym_diff": result = sym_diff(a, b) operation_name = "симметрической разности" - elif operation == 'cartesian': + elif operation == "cartesian": result = cartesian(a, b) operation_name = "декартового произведения" - + # Форматирование результата - if operation == 'cartesian': - result_text = f"Результат {operation_name}: {', '.join(str(x) for x in result)}" + if operation == "cartesian": + result_text = ( + f"Результат {operation_name}: {', '.join(str(x) for x in result)}" + ) else: result_text = f"Результат {operation_name}: {', '.join(str(x) for x in sorted(result))}" @@ -45,36 +50,46 @@ def index(): if a and b: img = plot_venn(a, b) - return render_template('index.html', img=img, result_text=result_text) + return render_template("index.html", img=img, result_text=result_text) -@app.route('/process', methods=['POST']) + +@app.route("/process", methods=["POST"]) def process(): data = request.get_json() - a = set(data.get('setA', '').replace(' ', '').split(',')) if data.get('setA') else set() - b = set(data.get('setB', '').replace(' ', '').split(',')) if data.get('setB') else set() - operation = data.get('operation', 'union') + a = ( + set(data.get("setA", "").replace(" ", "").split(",")) + if data.get("setA") + else set() + ) + b = ( + set(data.get("setB", "").replace(" ", "").split(",")) + if data.get("setB") + else set() + ) + operation = data.get("operation", "union") # Выбор операции - if operation == 'union': + if operation == "union": result = union(a, b) - elif operation == 'intersection': + elif operation == "intersection": result = intersection(a, b) - elif operation == 'difference': + elif operation == "difference": result = difference(a, b) - elif operation == 'sym_diff': + elif operation == "sym_diff": result = sym_diff(a, b) - elif operation == 'cartesian': + elif operation == "cartesian": result = cartesian(a, b) else: result = set() # Если результат — множество кортежей (для декартового произведения), конвертируем в список строк - if operation == 'cartesian': + if operation == "cartesian": result_list = [str(t) for t in result] else: result_list = list(result) - return jsonify({'result': result_list}) + return jsonify({"result": result_list}) + -if __name__ == '__main__': +if __name__ == "__main__": app.run(debug=True) diff --git a/static/style.css b/static/style.css index 55fa284..6de513f 100644 --- a/static/style.css +++ b/static/style.css @@ -1,18 +1,134 @@ body { - font-family: Arial, sans-serif; - max-width: 600px; - margin: 50px auto; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + max-width: 900px; + margin: 0 auto; padding: 20px; - background-color: #f9f9f9; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + color: #333; } -input, select, button { +.container { + background: white; + border-radius: 20px; + padding: 40px; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + margin-top: 20px; +} + +h1 { + text-align: center; + color: white; + margin-bottom: 10px; + font-size: 2.8em; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); + font-weight: 700; +} + +.subtitle { + text-align: center; + color: white; + margin-bottom: 30px; + font-size: 1.2em; + opacity: 0.9; +} + +.form-group { + margin-bottom: 25px; +} + +label { + display: block; + margin-bottom: 10px; + font-weight: 600; + color: #555; + font-size: 1.1em; +} + +input, +select, +button { display: block; - margin: 10px 0; - padding: 8px; + margin: 8px 0; + padding: 15px; width: 100%; + border: 2px solid #e1e8ed; + border-radius: 12px; + font-size: 16px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-sizing: border-box; + font-family: inherit; +} + +input:focus, +select:focus { + outline: none; + border-color: #667eea; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15); + transform: translateY(-2px); } button { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + padding: 18px; + font-size: 18px; + font-weight: 600; cursor: pointer; + margin-top: 25px; + border-radius: 12px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +button:hover { + transform: translateY(-3px); + box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4); } + +#result { + background: #f8f9fa; + /* Нейтральный серый фон */ + color: #2d3436; + /* Темно-серый текст */ + border-left: 5px solid #667eea; + padding: 20px; + margin: 30px 0; + border-radius: 12px; + font-size: 1.2em; + font-weight: 500; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08); +} + +.venn-container { + text-align: center; + margin: 40px 0; + padding: 30px; + background: #f8f9fa; + border-radius: 15px; + box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.05); +} + +.venn-container h2 { + color: #2d3436; + margin-bottom: 25px; + font-size: 1.8em; + font-weight: 600; +} + +.venn-container img { + max-width: 100%; + height: auto; + border-radius: 15px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); + border: 3px solid white; +} + +.operation-info { + text-align: center; + color: #636e72; + font-style: italic; + margin-top: 10px; + font-size: 0.9em; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 26b5b4c..c726ed7 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,39 +1,57 @@ + - Визуализация множеств + + Визуализация операций над множествами +

Визуализация операций над множествами

+
Интерактивная диаграмма Венна с отображением элементов
-
- - +
+ +
+ + +
- - +
+ + +
- +
+ + +
- - + + - {% if result_text %} -

{{ result_text }}

- {% endif %} + {% if result_text %} +
{{ result_text }}
+ {% endif %} - {% if img %} -

Диаграмма Венна:

- Диаграмма Венна - {% endif %} + {% if img %} +
+ Диаграмма Венна +
Элементы отображаются непосредственно в соответствующих областях диаграммы
+
+ {% endif %} +
- + + \ No newline at end of file diff --git a/venn_visual.py b/venn_visual.py index 08d5709..e28ee85 100644 --- a/venn_visual.py +++ b/venn_visual.py @@ -1,81 +1,88 @@ # venn_visual.py import matplotlib -matplotlib.use('Agg') # <- критично! Без оконного backend + +matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib_venn import venn2 import io import base64 -def plot_venn(a, b, set_labels=('A', 'B')): + +def plot_venn(a, b, set_labels=("A", "B")): """ Создаёт красивую диаграмму Венна для двух множеств с отображением элементов. """ # Создаём фигуру с современным дизайном - plt.figure(figsize=(8, 8), facecolor='#f8f9fa') + plt.figure(figsize=(8, 8), facecolor="#f8f9fa") ax = plt.gca() - ax.set_facecolor('#f8f9fa') - + ax.set_facecolor("#f8f9fa") + # Цветовая схема - colors = ['#ff6b6b', '#4ecdc4'] - text_color = '#2d3436' - + colors = ["#ff6b6b", "#4ecdc4"] + text_color = "#2d3436" + # Строим диаграмму Венна - v = venn2([a, b], set_labels=set_labels, - set_colors=colors, alpha=0.8) - + v = venn2([a, b], set_labels=set_labels, set_colors=colors, alpha=0.8) + # Очищаем стандартные подписи с количествами и добавляем элементы for label in v.subset_labels: if label: - label.set_text('') - + label.set_text("") + # Добавляем элементы множества A только (левая часть) if a - b: elements_a = sorted(a - b) - v.get_label_by_id('10').set_text('\n'.join(map(str, elements_a))) - + v.get_label_by_id("10").set_text("\n".join(map(str, elements_a))) + # Добавляем элементы пересечения A∩B (центральная часть) if a & b: elements_intersect = sorted(a & b) - v.get_label_by_id('11').set_text('\n'.join(map(str, elements_intersect))) - + v.get_label_by_id("11").set_text("\n".join(map(str, elements_intersect))) + # Добавляем элементы множества B только (правая часть) if b - a: elements_b = sorted(b - a) - v.get_label_by_id('01').set_text('\n'.join(map(str, elements_b))) - + v.get_label_by_id("01").set_text("\n".join(map(str, elements_b))) + # Настраиваем подписи множеств for label in v.set_labels: if label: label.set_fontsize(16) - label.set_fontweight('bold') + label.set_fontweight("bold") label.set_color(text_color) - + # Настраиваем подписи элементов for subset_label in v.subset_labels: if subset_label: subset_label.set_fontsize(12) - subset_label.set_fontweight('normal') + subset_label.set_fontweight("normal") subset_label.set_color(text_color) - + # Настраиваем внешний вид окружностей for patch in v.patches: if patch: - patch.set_edgecolor('#2d3436') + patch.set_edgecolor("#2d3436") patch.set_linewidth(2) - patch.set_linestyle('-') - + patch.set_linestyle("-") + # УБРАЛ ЗАГОЛОВОК ДИАГРАММЫ # plt.title('Диаграмма Венна', color=text_color, fontsize=18, pad=20, fontweight='bold') - + # Убираем оси для чистого вида - ax.axis('off') - + ax.axis("off") + # Сохраняем с высоким качеством buf = io.BytesIO() - plt.savefig(buf, format='png', bbox_inches='tight', - facecolor=ax.get_facecolor(), dpi=150, - transparent=False, edgecolor='none') + plt.savefig( + buf, + format="png", + bbox_inches="tight", + facecolor=ax.get_facecolor(), + dpi=150, + transparent=False, + edgecolor="none", + ) plt.close() - - img_base64 = base64.b64encode(buf.getvalue()).decode('utf-8') + + img_base64 = base64.b64encode(buf.getvalue()).decode("utf-8") return img_base64 From 62700d61d44c336683b0e9f3acf3a81e5b3807b4 Mon Sep 17 00:00:00 2001 From: 6ixyld <128928774+6ixyld@users.noreply.github.com> Date: Wed, 29 Oct 2025 00:21:01 +0300 Subject: [PATCH 10/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebe891f..a3059c4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## Установка ```bash -git clone https://github.com//sets-visualizer.git +git clone https://github.com/6ixyld/sets-visualizer.git cd sets-visualizer python -m venv venv source venv/bin/activate # или venv\Scripts\activate на Windows