Добавляем картинки и пишем тамагочи

Как добавить картинку в окошко?

На самом деле все просто, достаточно просто добавить 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()

Больше про TkInter

https://metanit.com/python/tkinter/