Добавляем картинки и пишем тамагочи
Как добавить картинку в окошко?
На самом деле все просто, достаточно просто добавить Label
и задать для него фоновое изображение. Картинку можно утащить отсюда. Вот так...
from tkinter import *
window = Tk()
# открываем картинку luntik.gif, она лежит в одной папке с программой и кладем в переменную luntik_image
luntik_image = PhotoImage(file='luntik.gif')
# создаем текстовое поле и указываем, что туда нужно поместить картинку из переменной luntik_image
Label(window, image=luntik_image).pack()
mainloop()
Но есть один нюанс: tkinter не умеет работать с большинством популярных форматов графики, из коробки доступен только gif. Однако эта проблема легко решается установкой стороннего модуля Pillow
!
Установка сторонних модулей
Если нам не хватает операций языка Python, а также встроенных модулей (библиотек), наступает тот самый момент, когда пора воспользоваться сторонними модулями.
Для установки модулей необходимо использовать программу pip, которая устанавливается на ваш компьютер вместе в питоном. Единственный нюанс, pip работает только через командную строку. Для ее вызова необходимо нажать Win+R
, а затем в окне "Выполнить" ввести cmd
и нажать Enter.
Для Mac вызов командной строки устроен аналогично, только вместо Win+R
нужно использоватьл комбинацию Command + пробел
, а затем в поле Spotlight ввести terminal.
Чтобы вызвать установщик пакетов, достаточно написать в консоли pip
(или pip3
, если не работает команда pip
) и нажать Enter
. Вы увидите что то похоже на...
Если команда
pip
не найдена, скорее всего при установке python вы не активировали галочку "Add to PATH".Есть два варианта решения проблемы: переустановить python или добавить путь к питону в PATH вручную.
Добавляя к команде pip
различные параметры, мы можем выполнять различные операции. Для начала давайте попробуем вывести список модулей, установленных на вашем компьютере. Для этого в командной строке необходимо ввести pip list
, а затем нажать клавишу Enter (клавиша Enter нажимается для любой операции в командной строке).
В ответ мы получим длинный список из двух колонок, как на картинке выше. Слева пишется имя модуля, а справа текущая версия этого модуля. Если хотим посмотреть, установлен ли у нас какой-то конкретный модуль, то нужно ввести pip show <название модуля>
. Для примера посмотрим, есть ли у меня модуль pillow
, который мы будем будем использовать для работы с графикой.
Как видишь, уже есть. Более того, в информации указана версия, автор и прочая информация. Если же модуль не установлен или при вводе названия допущена ошибка, то информация не будет выведена.
Наличие больших и маленьких букв в названии модуля роли не играет. Даже если ввести pip show PiLloW
, pip все равно вывел бы информацию о нем.
Если какой-то модуль нам больше не нужен или возникла какая-то неполадка в его работе, то мы можем его удалить, введя pip uninstall <название модуля>
.
Ничего сложного! А если я хочу обратно его установить, то мне нужно ввести pip install <название модуля>
.
Вот и все. После установки, можно спокойно импортировать модули так же, как это было показано в предыдущих параграфах.
Мы перечислили некоторые самые важные возможности pip
, хотя это далеко не полный их перечень. Для более подробного знакомства обратитесь к документации pip.
Поиск подходящих вам модулей можно осуществлять не только на в поисковике (Google, Yandex и т.д.), в качестве базы для поиска можно использовать Pypi, LDF или GitHub. Pypi содержит в себе поисковик, который позволяет отыскать не только модули, но и любые их модификации. После их можно установить либо по называнию pip install <название модулся>
, либо скачать файл *.whl
, который содержит ваш модуль.
Установка из файла аналогичена установке по названию. Сначала вам нужно вам нужно переместится в папку, где хранится файл *.whl
, используя команду cd <путь к вашей папке>
, а затем применить команду pip install <название файла.whl>
. На LDF хранятся модули в формате *.whl
, некоторые из которых уже нельзя найти на Pypi.
Также необходимый модуль можно установить из исходников с помощью команды pip install https://github.com/<логин владельца репозитория>/<название репозитория>/<ветвь.zip>
. Так например, мы можем поставить pillow
вот такой командой: pip install https://github.com/python-pillow/Pillow/master.zip
.
Используем картинки любых форматов и меняем их размер
Итак, удобнее всего открывать картинки с использованием библиотеки для работы с графикой Pillow.
https://pillow.readthedocs.io/
Сперва библиотеку нужно установить. Для этого в консоли нужно выполнить команду:
pip3 install pillow
Если консоль попросит права администратора, можно установить библиотеку только для своего пользователя.
pip3 install --user pillow
После этого можно открывать файлы в любых форматах.
from tkinter import *
# импортируем функционал для работы с картинками из pillow, которую мы только что установили
from PIL import Image, ImageTk
window = Tk()
# открываем изображение любого формата и кладем его в переменную image_file
image_file = Image.open("brothers.jpeg")
# "готовим" изображение к размещению в окне и кладем в переменную vp_image
vp_image = ImageTk.PhotoImage(image_file)
# добавляем после и устанавливаем картинку в качестве фона
Label(window, image=vp_image).pack()
mainloop()
В качестве бонуса - изменить размер картинки теперь можно прямо в коде.
# открываем изображение любого формата и кладем его в переменную image_file
image_file = Image.open("brothers.jpeg")
# меняем размер картинки на 300 на 300 пикселей
image_file = image_file.resize((300, 300), Image.LANCZOS)
# "готовим" изображение к размещению в окне и кладем в переменную vp_image
vp_image = ImageTk.PhotoImage(image_file)
Используем таймер, цвет и размер шрифта
Сейчас наше приложение умеет реагировать только на нажати кнопок, но часто приложениям требуется обновиться самостоятельно. Например, раз в несколько секунд.
Для этого можно попросить tkinter
вызвать нашу функцию не после нажатия на кнопку, а просто через несколько миллисекунд или секунд. Делается это с помощью метода after()
. В примере ниже попробуем обновлять случайное число раз в секунду.
import random
from tkinter import *
# функция, которая будет обновлять число на экране
def update_number():
# генерируем и записываем число в текстовое поле
label.config(text=random.randint(1, 100))
# просим window вызвать функцию update_number еще раз через секунду (1000 мс)
window.after(1000, update_number)
window = Tk()
# создаем текстовое поле
# дополнительно указываем шрифт (семейство - Comic Sans MS, 25 - размер) и цвет (fg - foreground color)
# padx и pady (padding x, padding y) в параметрах pack - это отступы от справа/слева и сверху/снизу
label = Label(window, font=("Comic Sans MS", 25), fg="green")
label.pack(padx=50, pady=50)
# просим window вызвать функцию update_number через секунду (1000 мс)
window.after(1000, update_number)
mainloop()
Программа выше будет раз в секунду менять число на экране.
Анимация
А что если совместить вывод изображений и таймер? Получится анимация!
Единственное, что нам потребуется для этого - научится изменять уже заданную картинку с помощью .configure()
.
Делается это так:
img1 = ImageTk.PhotoImage(Image.open("face1.jpg"))
img2 = ImageTk.PhotoImage(Image.open("face2.jpg"))
image_label = Label(window, image=img1)
image_label.pack()
# когда хотим поменять картинку - вызываем configure
image_label.configure(image=img2)
Ниже пример простой анимации - лицо меняется каждые 800 мс.
from tkinter import *
from PIL import Image, ImageTk
import psutil
# функция, которая будет двигать картинки
def next_img():
global pic_number
# если номер был 0 - он станет 1, а если был 1 - станет 0
pic_number = (pic_number + 1) % 2
# в зависимости от номера, выбираем нужную картинку
if pic_number == 0:
# и устанавливаем ее в image_label
image_label.configure(image=img1)
if pic_number == 1:
image_label.configure(image=img2)
# через долю секунды - вызываем смену картинки снова
window.after(800, next_img)
window = Tk()
window.title("FaceIt")
# открываем две картинки и изменяем их размер до 300 пикселей
img1 = ImageTk.PhotoImage(Image.open("face1.jpg").resize((300, 300), Image.ANTIALIAS))
img2 = ImageTk.PhotoImage(Image.open("face2.jpg").resize((300, 300), Image.ANTIALIAS))
# тут будем хранить номер картинки, которую показываем сейчас
pic_number = 0
# чтобы иметь возможность менять картинку, сохраняем поле в переменную image_label
image_label = Label(window, image=img1)
image_label.pack()
# спустя долю секунды просим поменять картинку - запустить функцию next_img
window.after(800, next_img)
mainloop()
Бонус! Как заставить тамагочи жить, даже если программа закрыта?
Можно вычислять состояние питомца исходя из времени прошлого кормления / игры / купания, а это время сохранять в файл. Для простоты давайте использовать отдельные файлы для каждого действия - кормежки / игры и тд. Ниже пример с едой, для остальных - по аналогии.
Как сохранить время действия в файл?
import time
# предположим, мы только что покормили питомца, фиксируем сколько сейчас времени
last_feed_time = time.time()
# открываем файл для записи
with open('last_feed_time.txt', 'w') as file:
# превращаем время в строку и записываем в файл
file.write(str(last_feed_time))
Как достать время последнего из файла?
import time
# если файл существует
try:
# открываем файл для чтения
with open('last_feed_time.txt', 'r') as file:
# и считываем время прошлой кормежки
last_feed_time = int(file.read())
# а если файла нет
except:
# считаем, что только покормили
last_feed_time = time.time()
Как вычислить сколько сейчас "жизни" исходя из прошедшего времени?
import time
# это значение нужно взфть из файла, для примера просто возьмем текущее время минус 40 секунд
last_feed_time = time.time() - 40
# максимум "жизни"
max_life = 100
# сколько секунд на одну единицу "жизни"
seconds_for_life_point = 10
# сколько времени прошло с прошлого кормления
elapsed_time = time.time() - last_feed_time
# жизнь не может быть меньше нуля
life = max(0, int(max_life - elapsed_time / seconds_for_life_point))
Бонус! Прогресс-бар для отображения статуса питомца.
from tkinter.ttk import *
from tkinter import *
from tkinter import messagebox
life = 100
# Функция для уменьшения жизни каждую секунду
def check_life():
global life
# уменьшаем жизнь
life -= 1
# узаписываем жизнь в прогрессбар
progress['value'] = life
# если жизни мало - закрываем приложение
if life <= 1:
messagebox.showerror(title="Вы проиграли!", message="Зверек умер... :(")
quit()
# иначе запускаем функция еще через секунду
else:
window.after(1000, check_life)
# функция для кормления
def feed():
global life
# устанавливаем жизнь на максимум
life = 100
progress['value'] = life
window = Tk()
window.title("Питомец")
# добавляем прогресс-бар
progress = Progressbar(window, value=life, maximum=100, length=200)
progress.pack(padx=25, pady=25)
# добавляем кнопку для увеличения жизни
Button(window, text="Покормить", command=feed).pack(padx=25, pady=25)
# включаем таймер для уменьшения жизни через секунду
window.after(1000, check_life)
mainloop()