Обработка и хранение данных / подсказка к 6 задачке

6

Доработать приложение, добавить возможность привязать к пользователю изображение. Работать должно примерно так

Попробуем теперь добавить картинку для студента.

Правда сначала нам надо поставить одну дополнительную библиотечку для работы с картинками. Библиотечка — это расширения для питона, которое можно скачать из интернета. Делается это следующим образом.

Сначала открываем папку где установлен питон и запускаем там WinPython Command Prompt

откроется консолька, вводим туда

pip install Pillow

правда в политехе доступ к интернету идет через прокси поэтому там надо ввести

pip install --proxy=http://172.27.100.5:4444 Pillow

и ждем пока пакет скачается, должны увидеть что-то в этом роде

теперь попробуем добавить картинку на форму.

Для вывода картинки используется простой label который мы обычно используем для текста, так вот в него можно передать картинку, корректируем код

class UI():
    def __init__(self, gui):    
        gui.geometry("550x170") # поменял ширину формы на 550
        
        # ...
            
        # добавил лейбл
        self.lblImage = Label(borderwidth=1, relief="solid") # чтобы лучше была видна граница картинки добавил рамочку
        self.lblImage.place(x=405, y=10, width=130, height=130) # ну и размеры подогнал

получится так:

теперь возьмем какую-нибудь картинку, вот такую, например, (нажать на нее правой кнопкой и выбрать “Сохранить картинку как…”)

и сохраним ее в папку, теперь ее должно быть видно слева

попробуем ее теперь вывести на форму. Делается это в несколько этапов:

from tkinter import *
import json
from PIL import Image, ImageTk  # добавляем объекты для работы с изображения из установленного выше пакета

def create_entry(x, y, label_text, entry_width):
    # ...

class UI():
    def __init__(self, gui):    
        # ...

        self.lblImage = Label(borderwidth=1, relief="solid")
        self.lblImage.place(x=405, y=10, width=130, height=130)
        
        # считаем изображение из файла
        # вообще сюда надо указать путь к файлу, 
        # но если файл лежит в той же папке что и файлик с кодом, то достаточно указать имя
        image = Image.open("067_160322763013645002.jpg")
        
        # и чтобы убедится, что изображение загрузилось попробуем вывести его размеры в консоль
        print(image.size) 

запускаем, и глядим в консоль:

ура! размеры определились, значит файл действительно загрузился в память.

Теперь передадим изображение в label. Тут немного хитро:

class UI():
    def __init__(self, gui):    
        # ...
        
        image = Image.open("067_160322763013645002.jpg")
        print(image.size)
        
        # создаем обертку для изображения которая нужна чтобы tkinter понял, что ему передали
        # в image у нас по сути просто сырые данные, ну а-ля как будто картина по номерам,
        # у которой краски отдельно, а на холсте еще никто не рисовал 
        # а photo_image это типа взяли и уже картинку нарисовали  
        self.photo_image = ImageTk.PhotoImage(image)
        
        # и теперь этот photo_image надо передать в label, ну по сути картинку поставить в рамочку
        self.lblImage.configure(image=self.photo_image)

проверяем:

красота! =)

Общая схема работы с изображением такое:

  1. Загрузить изображение в память из файла через Image.open("067_160322763013645002.jpg")
  2. Собрать изображение в вид пригодный для tkinter через ImageTk.PhotoImage(image)
  3. Положить изображение в label через self.lblImage.configure(image=self.photo_image)

Правда чет изображение какое-то здоровенное…

Вообще это стандартное поведение при выводе изображения, никто за нас его уменьшать не будет.

Сейчас мы по сути мы видим центральную часть изображения в исходном размере:

чтобы изображение уменьшить надо сделать это вручную, делается это между 1 и 2 пунктом

  1. Загрузить изображение в память из файла через Image.open("067_160322763013645002.jpg")
  2. Уменьшить изображение
  3. Собрать изображение в вид пригодный для tkinter через ImageTk.PhotoImage(image)
  4. Положить изображение в label через self.lblImage.configure(image=self.photo_image)

всего одной строчкой:

class UI():
    def __init__(self, gui):    
        # ...
        
        # это не трогаем
        image = Image.open("067_160322763013645002.jpg")
        print(image.size)
        
        # изменяем размеры, первым параметром передаем список из двух элементов [130, 130]
        # это размеры по ширине и по длине, они должны совпадать с размером label
        # второй параметр Image.ANTIALIAS это алгоритм по которому будут меняться размеры, ANTIALIAS обеспечивает плавный переход цветов пикселей
        image.thumbnail([130, 130], Image.ANTIALIAS)
        
        # это тоже не трогаем
        self.photo_image = ImageTk.PhotoImage(image)
        self.lblImage.configure(image=self.photo_image)

запускаем:

прекрасно! =)

Сделаем теперь чтобы можно было бы кликнуть на изображение, затем открылось окно для выбора файла и можно было бы поменять картинку.

Сначала привяжем реакцию на клик по label. Что-то похожее мы уже делали:

class UI():
    def __init__(self, gui):    
        # ... 
        
        self.photo_image = ImageTk.PhotoImage(image)
        self.lblImage.configure(image=self.photo_image)
        
        # привязал функцию к клику на label
        self.lblImage.bind("<Button>", self.on_lbl_image_click)
        
    def on_lbl_image_click(self, event):
        # при клике вывожу сообщение в консоль
        print("нажал на картинку")

тестируем:

теперь сделаем так чтобы открывалось диалоговое окно в котором можно выбрать файл. Делается это так:

from tkinter import *
import json
from PIL import Image, ImageTk
import tkinter.filedialog # добавил пакет для работы с диалоговыми окнами

def create_entry(x, y, label_text, entry_width):
    # ...

class UI():
    def __init__(self, gui):    
        # ...
        
    def on_lbl_image_click(self, event):
        # открываем окно для выбора файла 
        image_file = tkinter.filedialog.askopenfile(
            filetypes = [ # тут указываем фильтры, чтобы по умолчанию позволял только картинки выбирать
                ("Картинки", "*.jpg;*.jpeg;*.png;*.gif"),
                ("Все файлы", "*.*")
            ]
        )
        # напечатаем путь к файлу который вернет askopenfile
        print(image_file.name)

проверяем:

получается, что image_file.name возвращает нам путь к файлу.

В принципе, теперь, чтобы открыть изображение достаточно просто добавить следующие строчки:

class UI():
    def __init__(self, gui):    
        # ...
        
    def on_lbl_image_click(self, event):
        # это не трогаем
        image_file = tkinter.filedialog.askopenfile(
            filetypes = [
                ("Картинки", "*.jpg;*.png;*.gif"),
                ("Все файлы", "*.*")
            ]
        )
        print(image_file.name)
        
        # грузим файлик в память используя путь в image_file.name
        image = Image.open(image_file.name)
        # уменьшаем до размеров лейбла
        image.thumbnail((130, 130), Image.ANTIALIAS)
        
        # ну и как обычно присваиваем изображение лейблу
        self.photo_image = ImageTk.PhotoImage(image)
        self.lblImage.configure(image=self.photo_image)

проверяем:

красота! =)