This site is developed to XHTML and CSS2 W3C standards. If you see this paragraph, your browser does not support those standards and you need to upgrade. Visit WaSP for a variety of options.

php pastebin - collaborative irc debugging view php source

Paste #675

Posted by: cross.py
Posted on: 2026-04-16 18:21:12
Age: 8 hrs ago
Views: 5
import tkinter as tk
from tkinter import messagebox, filedialog

class CrosswordApp:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Генератор кроссворда — центральное слово вертикально + легенда")
        self.root.geometry("1250x820")

        # Главный контейнер с Canvas и Scrollbar
        self.main_canvas = tk.Canvas(self.root)
        self.scrollbar = tk.Scrollbar(self.root, orient="vertical", command=self.main_canvas.yview)
        self.scrollable_frame = tk.Frame(self.main_canvas)

        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.main_canvas.configure(scrollregion=self.main_canvas.bbox("all"))
        )

        self.main_canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        self.main_canvas.configure(yscrollcommand=self.scrollbar.set)

        # Размещаем элементы
        self.main_canvas.pack(side="left", fill="both", expand=True)
        self.scrollbar.pack(side="right", fill="y")

        # ====================== ВВОД ДАННЫХ ======================
        input_frame = tk.Frame(self.scrollable_frame, padx=20, pady=15)
        input_frame.pack(fill="x")

        tk.Label(input_frame, text="Генератор кроссворда\n(центральное слово — вертикально по середине)", 
                 font=("Arial", 18, "bold")).pack(pady=10)

        # Центральное слово
        tk.Label(input_frame, text="Центральное слово (будет ВЕРТИКАЛЬНЫМ):", 
                 font=("Arial", 12, "bold")).pack(anchor="w", pady=(10, 0))
        self.central_entry = tk.Entry(input_frame, width=85, font=("Arial", 14), justify="center")
        self.central_entry.pack(pady=8)
        
        tk.Button(input_frame, text="✅ Задать центральное слово", 
                  command=self.set_central_word, 
                  font=("Arial", 12), bg="#4CAF50", fg="white", height=1).pack(pady=5)

        self.central_word = None

        # Горизонтальные слова
        tk.Label(input_frame, text="Горизонтальные слова (по одному на строку):", 
                 font=("Arial", 12)).pack(anchor="w", pady=(15, 0))
        self.other_words_text = tk.Text(input_frame, height=7, width=90, font=("Arial", 11))
        self.other_words_text.pack(pady=5)

        # Подсказки
        tk.Label(input_frame, text="Подсказки (по одной на строку, в порядке горизонтальных слов):", 
                 font=("Arial", 12)).pack(anchor="w", pady=(10, 0))
        tk.Label(input_frame, text="Пример: Столица Франции   или   1. По горизонтали: Столица Франции", 
                 font=("Arial", 10), fg="gray").pack(anchor="w")
        self.clues_text = tk.Text(input_frame, height=10, width=90, font=("Arial", 11))
        self.clues_text.pack(pady=5)

        # Кнопка генерации
        tk.Button(input_frame, text="🚀 СГЕНЕРИРОВАТЬ КРОССВОРД", 
                  command=self.generate_crossword, 
                  font=("Arial", 15, "bold"), bg="#2196F3", fg="white", height=2).pack(pady=20)

        # ====================== КНОПКИ ЭКСПОРТА ======================
        export_frame = tk.Frame(self.scrollable_frame, pady=10)
        export_frame.pack()

        self.export_filled_btn = tk.Button(
            export_frame, 
            text="💾 Экспорт ЗАПОЛНЕННОГО SVG (с буквами + легенда)",
            command=lambda: self.export_svg(filled=True),
            font=("Arial", 12), bg="#FF9800", fg="white", state="disabled", height=2
        )
        self.export_filled_btn.pack(side="left", padx=15)

        self.export_puzzle_btn = tk.Button(
            export_frame, 
            text="💾 Экспорт ПУСТОГО SVG (подсказки, стрелки + легенда)",
            command=lambda: self.export_svg(filled=False),
            font=("Arial", 12), bg="#9C27B0", fg="white", state="disabled", height=2
        )
        self.export_puzzle_btn.pack(side="left", padx=15)

        # ====================== ОБЛАСТЬ ПРЕДПРОСМОТРА ======================
        self.preview_frame = tk.Frame(self.scrollable_frame, bg="#f5f5f5", pady=15)
        self.preview_frame.pack(fill="both", expand=True, padx=20, pady=10)

        self.canvas = None
        self.shifted_grid = None
        self.grid_height = 0
        self.grid_width = 0
        self.word_placements = None
        self.clues_list = None

        # Привязка колесика мыши к скроллу
        self.main_canvas.bind_all("<MouseWheel>", self._on_mousewheel)

        self.root.mainloop()

    def _on_mousewheel(self, event):
        self.main_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    def set_central_word(self):
        word = self.central_entry.get().strip().upper().replace(" ", "")
        if not word or not word.isalpha():
            messagebox.showerror("Ошибка", "Введите корректное центральное слово (только буквы)!")
            return
        self.central_word = word
        messagebox.showinfo("Готово", f"Центральное слово сохранено:\n{word}\nОно будет размещено вертикально по середине.")

    def generate_crossword(self):
        if not self.central_word:
            messagebox.showerror("Ошибка", "Сначала задайте центральное слово!")
            return

        # Получаем горизонтальные слова
        other_text = self.other_words_text.get("1.0", tk.END).strip()
        other_words = [line.strip().upper().replace(" ", "") 
                       for line in other_text.splitlines() 
                       if line.strip() and line.strip().isalpha()]

        if not other_words:
            messagebox.showerror("Ошибка", "Введите хотя бы одно горизонтальное слово!")
            return

        # Получаем подсказки
        clues_raw = self.clues_text.get("1.0", tk.END).strip().splitlines()
        self.clues_list = []
        for line in clues_raw:
            clue = line.strip()
            if not clue:
                continue
            # Убираем номера в начале
            if clue[0].isdigit():
                for i, ch in enumerate(clue):
                    if ch.isalpha() or ch == '«' or ch == '"':
                        self.clues_list.append(clue[i:].strip())
                        break
            else:
                self.clues_list.append(clue)

        # ====================== ПОСТРОЕНИЕ КРОССВОРДА ======================
        central_list = list(self.central_word)
        n_rows_central = len(central_list)

        placements = {}
        used_rows = set()
        placed = []
        not_placed = []

        for word in other_words:
            found = False
            for row_idx in range(n_rows_central):
                if row_idx in used_rows:
                    continue
                for idx in range(len(word)):
                    if word[idx] == central_list[row_idx]:
                        placements[row_idx] = (word, idx)
                        used_rows.add(row_idx)
                        placed.append(word)
                        found = True
                        break
                if found:
                    break
            if not found:
                not_placed.append(word)

        # Построение сетки
        grid = {}
        word_placements = []

        mid_col = 12
        central_start_row = 8

        # Центральное слово (вертикально)
        for i, letter in enumerate(central_list):
            r = central_start_row + i
            c = mid_col
            grid[(r, c)] = letter

        word_placements.append({
            'dir': 'down',
            'start_r': central_start_row,
            'start_c': mid_col,
            'word': self.central_word,
            'clue': "Центральное слово (вертикально)"
        })

        # Горизонтальные слова
        clue_idx = 0
        for central_r_idx, (word, match_idx) in placements.items():
            r = central_start_row + central_r_idx
            start_c = mid_col - match_idx
            for j, letter in enumerate(word):
                c = start_c + j
                grid[(r, c)] = letter

            clue = self.clues_list[clue_idx] if clue_idx < len(self.clues_list) else f"Слово: {word}"
            word_placements.append({
                'dir': 'across',
                'start_r': r,
                'start_c': start_c,
                'word': word,
                'clue': clue
            })
            clue_idx += 1

        # Сдвиг координат
        rows_list = [r for r, c in grid.keys()]
        cols_list = [c for r, c in grid.keys()]
        min_r = min(rows_list)
        min_c = min(cols_list)

        shift_r = -min_r + 4
        shift_c = -min_c + 4

        self.shifted_grid = {(r + shift_r, c + shift_c): letter for (r, c), letter in grid.items()}
        self.grid_height = (max(rows_list) - min_r + 9)
        self.grid_width = (max(cols_list) - min_c + 9)

        for p in word_placements:
            p['start_r'] += shift_r
            p['start_c'] += shift_c

        # Нумерация
        word_placements.sort(key=lambda x: (x['start_r'], x['start_c']))
        for num, p in enumerate(word_placements, 1):
            p['number'] = num

        self.word_placements = word_placements

        # ====================== ОТРИСОВКА ======================
        if self.canvas:
            self.canvas.destroy()

        cell_size = 48
        canvas_w = self.grid_width * cell_size + 40
        canvas_h = self.grid_height * cell_size + 40

        self.canvas = tk.Canvas(self.preview_frame, width=canvas_w, height=canvas_h,
                                bg="#eeeeee", highlightthickness=0)
        self.canvas.pack(pady=10)

        for row in range(self.grid_height):
            for col in range(self.grid_width):
                x1 = col * cell_size + 20
                y1 = row * cell_size + 20
                x2 = x1 + cell_size
                y2 = y1 + cell_size

                key = (row, col)
                if key in self.shifted_grid:
                    self.canvas.create_rectangle(x1, y1, x2, y2, fill="#ffffff", outline="#333333", width=3)
                    self.canvas.create_text((x1 + x2)//2, (y1 + y2)//2,
                                            text=self.shifted_grid[key],
                                            font=("Arial", 24, "bold"), fill="#1a1a1a")
                else:
                    self.canvas.create_rectangle(x1, y1, x2, y2, fill="#1a1a1a", outline="#1a1a1a")

        # Активация кнопок экспорта
        self.export_filled_btn.config(state="normal")
        self.export_puzzle_btn.config(state="normal")

        messagebox.showinfo("Готово!", 
                            f"Кроссворд сгенерирован!\n"
                            f"Центральное слово: {self.central_word}\n"
                            f"Размещено горизонтальных слов: {len(placed)}")

    def export_svg(self, filled=True):
        if not self.shifted_grid:
            messagebox.showerror("Ошибка", "Сначала сгенерируйте кроссворд!")
            return

        default_name = f"кроссворд_{self.central_word}_{'filled' if filled else 'puzzle'}.svg"
        filename = filedialog.asksaveasfilename(
            defaultextension=".svg",
            filetypes=[("SVG файлы", "*.svg")],
            initialfile=default_name
        )
        if not filename:
            return

        cell_size = 65
        padding = 60
        grid_w = self.grid_width * cell_size
        grid_h = self.grid_height * cell_size
        legend_x = grid_w + padding + 100
        total_w = legend_x + 520
        total_h = max(grid_h + 120, 900)

        svg = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="{total_w}" height="{total_h}" viewBox="0 0 {total_w} {total_h}">
  <defs>
    <style>
      text {{ font-family: Arial, sans-serif; }}
      .letter {{ font-size: 38px; font-weight: bold; fill: #111; }}
      .number {{ font-size: 19px; font-weight: bold; fill: #000; }}
      .clue {{ font-size: 21px; fill: #222; }}
      .title {{ font-size: 26px; font-weight: bold; fill: #000; }}
    </style>
  </defs>
'''

        # Сетка
        for row in range(self.grid_height):
            for col in range(self.grid_width):
                x = padding + col * cell_size
                y = padding + row * cell_size
                key = (row, col)

                if key in self.shifted_grid:
                    svg += f'  <rect x="{x}" y="{y}" width="{cell_size}" height="{cell_size}" fill="#ffffff" stroke="#333" stroke-width="5"/>\n'
                    if filled:
                        letter = self.shifted_grid[key]
                        svg += f'  <text x="{x + cell_size/2}" y="{y + cell_size/2 + 12}" text-anchor="middle" dominant-baseline="middle" class="letter">{letter}</text>\n'

                    start_info = next((p for p in self.word_placements if p.get('start_r') == row and p.get('start_c') == col), None)
                    if start_info:
                        num = start_info['number']
                        svg += f'  <text x="{x + 8}" y="{y + 26}" class="number">{num}</text>\n'
                        if not filled:
                            arrow = "→" if start_info['dir'] == "across" else "↓"
                            svg += f'  <text x="{x + cell_size - 25}" y="{y + cell_size - 15}" font-size="34" fill="#666" text-anchor="end">{arrow}</text>\n'
                else:
                    svg += f'  <rect x="{x}" y="{y}" width="{cell_size}" height="{cell_size}" fill="#1a1a1a" stroke="#1a1a1a"/>\n'

        # Легенда
        y = padding + 40
        svg += f'  <text x="{legend_x}" y="{y}" class="title">ПО ГОРИЗОНТАЛИ (Across)</text>\n'
        y += 60

        for p in self.word_placements:
            if p['dir'] == 'across':
                svg += f'  <text x="{legend_x}" y="{y}" class="clue">{p["number"]}. {p["clue"]}</text>\n'
                y += 38

        y += 50
        svg += f'  <text x="{legend_x}" y="{y}" class="title">ПО ВЕРТИКАЛИ (Down)</text>\n'
        y += 55

        for p in self.word_placements:
            if p['dir'] == 'down':
                svg += f'  <text x="{legend_x}" y="{y}" class="clue">{p["number"]}. {p["clue"]}</text>\n'
                y += 38

        svg += '</svg>'

        try:
            with open(filename, "w", encoding="utf-8") as f:
                f.write(svg)
            mode_text = "ЗАПОЛНЕННЫЙ (с буквами)" if filled else "ПУСТОЙ (с подсказками, стрелками и легендой)"
            messagebox.showinfo("Успешно!", f"SVG-файл сохранён:\n{filename}\n\nРежим: {mode_text}")
        except Exception as e:
            messagebox.showerror("Ошибка сохранения", str(e))


if __name__ == "__main__":
    app = CrosswordApp()

Download raw | Create new paste

© BitByByte, 2026.
Downgrade Counter