Мой брат – поэт.
Меня всегда восхищало, как человек может взять и написать такие строчки, что заиграют струны души, да ещё и в рифму!
День рождения брата, мне 10, гитара у груди (играть я совершенно не умел), его стихотворение перед глазами и я пою с улыбкой и свежестью, как пел гимн школы на первое сентября.
Прошло 8 лет… Я подрос, вырос и брат. И вот приближается его день рождения. Внутри меня порыв, внутри меня желание… поздравить, но как-то с изюминкой, как-то… с душой что ли.
Я заканчиваю школу, имею небольшой опыт программирования на Python, но большое желание претворять идеи в жизнь. И лампочка в голове зажигается – поэтическая викторина!
Идея
Выводится строка стихотворения, а ниже – несколько вариантов ответов (различные строки) и нужно выбрать правильный ответ (то есть – правильное продолжение стихотворения).
Если игрок допускает ошибку, то игра завершается и ему предлагается начать заново, причём стихотворение (которое нужно отгадать) выбирается случайным образом.
Создание поэтической викторины
Шаг первый – графическое представление
Начать я решил с описания схемы программы:
-
Как она должна выглядеть
-
Какие есть окна
-
Какие доступны возможности
Решил, что будет три окна:
-
Основное, где будут располагаться кнопки для начала игры, открытия меню стихотворений и закрытия программы
-
Меню стихотворений, в котором можно посмотреть и прочитать все доступные в игре стихотворения
-
Игровое окно, в котором выводится строка стихотворения, а также варианты ответа и возможность сменить стихотворение (если текущее не нравится)
Вторая ступенька – написание программы
Не стал мудрить и решил использовать то, что знаю – Python. Для графического интерфейса выбрал модуль tkinter.
Во избежание конфликтов из-за версий модулей решил работать в виртуальном окружении. Для размещения кода проекта выбрал GitHub.
Структура проекта:
-
venv – директория для виртуального окружения
-
.gitignore – файл для игнорирования определённых файлов и директорий при отправлении изменений через Git
-
constants.py – файл с константами
-
main.py – файл для запуска программы
-
poetry.py – файл со стихотворениями
-
README.md – файл описывающий проект
-
ui.py – файл с логикой работы UI
В .gitignore добавил:
.idea /venv/
constants.py
MAIN_BUTTON_FONT = "Arial 24" ANSWER_BUTTON_FONT = "Arial 20" BLACK = "black" WHITE = "white"
main.py:
from ui import UI if __name__ == "__main__": ui = UI() ui.start_ui()
Для стихотворений выбрал тип данных Python – словарь.
Название стихотворения является ключом словаря, а текст – значением. Текст стихотворения оформлен в виде списка. Элементы списка – строки стихотворения.
poetry.py:
poetry_dict = { "название_вашего_стихотворения": [ "Первая строка стихотворения,", "Вторая строка стихотворения.", ], "Всегда": [ "Всегда,", "Что-то происходит.", "Взлёты и падения,", "Когда-то проходят.", "Возможно научиться их ценить,", "Учит наша короткая жизнь.", ], "Dark night": [ "Yeah, I guess I’m alright,", "Writing some notes,", "To get rid of my thoughts,", "The past and future all combined,", "Been heavy lately on my mind,", "I am sure it’s temporary,", "No need more worries,", "The good side actually,", "I will be able to tell more stories.", ], }
Наибольшая часть кода расположена в файле ui.py.
Решил сделать UI через класс и использовать self и статичные методы.
Понадобились следующие импорты:
import random import tkinter as tk from tkinter import ttk from constants import ANSWER_BUTTON_FONT, BLACK, MAIN_BUTTON_FONT, WHITE from poetry import poetry_dict
Описание инициализации:
class UI: def __init__(self): self.root = tk.Tk() self.root.title("Poetry Quiz") self.root.configure(background="gray") self.root.geometry("900x600")
Добавляем необходимые переменные:
self.poetry_part = tk.StringVar() self.poetry_titles = [element for element in poetry_dict.keys()] self.poetry_showing_name = "" self.poetry_showing = [] self.step = 0 self.last_step = 0 self.mistakes = 0
Создаём UI-элементы (текст, кнопки, комбинированный список):
self.poetry_label = tk.Label( self.root, textvariable=self.poetry_part, font="Arial 26" ) self.start_label = tk.Label(self.root, text="Poetry Quiz", font="Arial 36") self.start_label.pack(fill="both", expand=True) self.start_button = tk.Button( self.root, text="Начать игру", background=WHITE, foreground=BLACK, highlightbackground="green", highlightthickness=2.5, font=MAIN_BUTTON_FONT, command=self.play_game, justify=tk.CENTER, ) self.start_button.pack(expand=True) self.poetry_button = tk.Button( self.root, text="Посмотреть стихотворения", background=WHITE, foreground=BLACK, highlightbackground="orange", highlightthickness=2.5, font=MAIN_BUTTON_FONT, command=self.open_poetry_menu, justify=tk.CENTER, ) self.poetry_button.pack(expand=True) self.exit_button = tk.Button( self.root, text="Завершить игру", background=WHITE, foreground=BLACK, highlightbackground="red", highlightthickness=2.5, font=MAIN_BUTTON_FONT, command=self.destroy_ui, justify=tk.CENTER, ) self.exit_button.pack(expand=True) self.another_poetry = tk.Button( self.root, background=WHITE, text="Выбрать другое стихотворение", foreground=BLACK, highlightbackground="orange", highlightthickness=2.5, font=MAIN_BUTTON_FONT, command=self.change_poetry, justify=tk.CENTER, ) self.first_answer_button = tk.Button( self.root, background=WHITE, foreground=BLACK, highlightbackground="green", highlightthickness=2.5, font=ANSWER_BUTTON_FONT, justify=tk.CENTER, ) self.first_answer_button.bind("<Button-1>", self.next_pick) self.second_answer_button = tk.Button( self.root, background=WHITE, foreground=BLACK, highlightbackground="green", highlightthickness=2.5, font=ANSWER_BUTTON_FONT, justify=tk.CENTER, ) self.second_answer_button.bind("<Button-1>", self.next_pick) self.third_answer_button = tk.Button( self.root, background=WHITE, foreground=BLACK, highlightbackground="green", highlightthickness=2.5, font=ANSWER_BUTTON_FONT, justify=tk.CENTER, ) self.third_answer_button.bind("<Button-1>", self.next_pick) self.poetry_choices = ttk.Combobox( self.root, values=self.poetry_titles, font="Arial 18" ) self.poetry_choices.set("Выберите стихотворение") self.poetry_choices.bind("<<ComboboxSelected>>", self.open_poetry) self.go_back_button = tk.Button( self.root, text="Назад", background=WHITE, foreground=BLACK, highlightbackground="red", highlightthickness=2.5, font=MAIN_BUTTON_FONT, command=self.go_back_to_menu, justify=tk.CENTER, )
Методы для создания и закрытия UI (при выходе из игры):
def start_ui(self) -> None: self.root.mainloop() def destroy_ui(self) -> None: self.root.destroy()
Расположение и скрытие UI-элементов:
@staticmethod def place_elements(*elements) -> None: for element in elements: element.pack(side="top", expand=2) @staticmethod def hide_elements(*elements) -> None: for element in elements: element.pack_forget()
Установка текста на кнопки для выбора ответа:
def set_all_buttons( self, poetry_showing_name: str, poetry_showing: list, step: int ) -> None: self.poetry_part.set(poetry_showing[step]) first_poetry = random.choice(list(poetry_dict.keys())) while first_poetry == poetry_showing_name: first_poetry = random.choice(list(poetry_dict.keys())) second_poetry = random.choice(list(poetry_dict.keys())) while second_poetry in [first_poetry, poetry_showing_name]: second_poetry = random.choice(list(poetry_dict.keys())) first_poetry = random.choice(poetry_dict[first_poetry]) second_poetry = random.choice(poetry_dict[second_poetry]) right_poetry = poetry_showing[step + 1] text = [first_poetry, second_poetry, right_poetry] for button in ( self.first_answer_button, self.second_answer_button, self.third_answer_button, ): text_for_button = random.choice(text) button.configure(text=text_for_button) del text[text.index(text_for_button)]
Запуск игры (при нажатии на кнопку «Начать игру»):
def play_game(self) -> None: self.hide_elements( self.start_label, self.start_button, self.exit_button, self.poetry_button, ) self.poetry_showing_name = random.choice(list(poetry_dict.keys())) self.poetry_showing = poetry_dict[self.poetry_showing_name] self.last_step = len(self.poetry_showing) self.poetry_part.set(self.poetry_showing[self.step]) self.poetry_label.configure(font="Arial 26") self.poetry_label.pack(fill="both", expand=True) self.set_all_buttons( poetry_showing_name=self.poetry_showing_name, poetry_showing=self.poetry_showing, step=self.step, ) self.place_elements( self.first_answer_button, self.second_answer_button, self.third_answer_button, self.another_poetry, self.go_back_button, )
Завершение игры (при победе или поражении):
def finish_game(self, lost: bool = False) -> None: self.hide_elements( self.first_answer_button, self.second_answer_button, self.third_answer_button, ) self.another_poetry.configure(text="Сыграть заново") if lost: self.poetry_part.set(f"Упс!\n\nВы допустили ошибку.\n\nПопробуйте заново.") else: self.poetry_part.set("Поздравляю!\n\nВы полностью угадали стихотворение!")
Открытие окна «Меню со стихотворениями»:
def open_poetry_menu(self) -> None: self.hide_elements( self.start_label, self.start_button, self.exit_button, self.poetry_button, ) self.poetry_choices.pack(side="top", expand=2) self.go_back_button.pack(side="bottom", expand=2)
Подготовка для вывода текста стихотворения (в меню стихотворений):
def build_poetry(self, poetry_rows: list) -> str: poetry = self.poetry_choices.get().upper() + "\n\n" return poetry + "\n".join(poetry_rows)
Вывод текста стихотворения при выборе элемента в комбинированном списке (Combo box):
def open_poetry(self, _: tk.Event) -> None: poetry_rows = poetry_dict[self.poetry_choices.get()] poetry = self.build_poetry(poetry_rows) self.poetry_part.set(poetry) self.poetry_label.configure(font="Arial 16") self.poetry_label.pack(fill="both", expand=True)
Возвращение в основное окно (при нажатии на кнопку «Назад»):
def go_back_to_menu(self) -> None: self.hide_elements( self.poetry_label, self.poetry_choices, self.first_answer_button, self.second_answer_button, self.third_answer_button, self.another_poetry, self.go_back_button, ) self.start_label.pack(fill="both", expand=True) self.place_elements(self.start_button, self.poetry_button, self.exit_button)
Кнопка «Выбрать другое стихотворение» и «Сыграть заново»:
def change_poetry(self) -> None: if self.another_poetry["text"] == "Сыграть заново": self.hide_elements( self.poetry_label, self.first_answer_button, self.second_answer_button, self.third_answer_button, self.another_poetry, self.go_back_button, ) self.another_poetry.configure(text="Выбрать другое стихотворение") self.poetry_label.configure(font="Arial 26") self.poetry_label.pack(fill="both", expand=True) self.first_answer_button.pack(side="top", expand=2) self.second_answer_button.pack(side="top", expand=2) self.third_answer_button.pack(side="top", expand=2) self.another_poetry.configure(text="Выбрать другое стихотворение") self.another_poetry.pack(side="top", expand=2) self.go_back_button.pack(side="top", expand=2) self.poetry_showing_name = random.choice(list(poetry_dict.keys())) self.poetry_showing = poetry_dict[self.poetry_showing_name] self.step = 0 self.set_all_buttons( self.poetry_showing_name, self.poetry_showing, self.step, ) self.last_step = len(self.poetry_showing)
Получение текста выбранного варианта ответа:
def get_clicked_button_text(self, button_id: str) -> str: return { "!button5": self.first_answer_button["text"], "!button6": self.second_answer_button["text"], "!button7": self.third_answer_button["text"], }[button_id]
Проверка ответа:
def is_correct_answer(self, button_id: str, step: int) -> bool: right_answer = self.poetry_showing[step] picked_answer = self.get_clicked_button_text(button_id) if right_answer != picked_answer: self.finish_game(True) return False return True
Переход на следующий шаг:
def next_pick(self, event: tk.Event) -> None: if self.step < (self.last_step - 2): self.step += 1 if self.is_correct_answer(event.widget._name, self.step): self.set_all_buttons( poetry_showing_name=self.poetry_showing_name, poetry_showing=self.poetry_showing, step=self.step, ) return self.finish_game()
Получилась такая программа:
Подведение итогов
Конечно, всегда есть куда стремиться.
Можно было бы ещё:
-
Сделать плавнее переход между окнами
-
Покрыть код тестами
-
И много чего другого
Однако в тот момент меня всё устроило и я рад был презентовать такой необычный подарок.
Благодарю за прочтение и желаю хорошего дня! =)
ссылка на оригинал статьи https://habr.com/ru/articles/869768/
Добавить комментарий