Рубрики
Без рубрики

Pysimplegui: рисунок текста на изображениях с Python GUI

Получите практические, реальные навыки Python на наших ресурсах и пути

Автор оригинала: Mike Driscoll.

Пакет подушек Позволяет нарисовать текст на изображениях с помощью Python. Это включает в себя использование шрифтов TrueType и OpenType. У вас есть много гибкости при добавлении этого текста на ваши изображения. Если вы хотите узнать больше, вы должны проверить Рисование текста на изображениях с подушкой и Python Отказ

Примечание. Код и шрифты, используемые в этом руководстве, можно найти в GitHub Repo для моей книги, подушка: обработка изображений с Python (глава 9)

Конечно, это приятнее, если вы действительно можете увидеть, как будет выглядеть ваш результат в режиме реального времени. Вы можете выполнить, что, написав графический пользовательский интерфейс (GUI), который позволяет вам применить разные шрифты и настраивать свои настройки. GUI, который вы создаете, позволит вашим пользователям изменять следующие свойства их текста:

  • Тип шрифта
  • Цвет шрифта
  • Размер шрифта
  • Положение текста

Когда ваш графический интерфейс закончен, это будет выглядеть так:

Давайте начнем писать какой-нибудь код!

Создание графического интерфейса

Для этого учебника вы будете использовать Pysimplegui Отказ Вы можете использовать Пип установить его. Как только вы закончите установку Pysimplegui, вы готовы пойти!

Теперь пришло время для того, чтобы комировать графический интерфейс. Откройте свой редактор Python и создайте новый файл. Тогда назовите это text_gui.py и введите следующий код:

# text_gui.py

import glob
import os
import PySimpleGUI as sg
import shutil
import tempfile

from PIL import Image
from PIL import ImageColor
from PIL import ImageDraw
from PIL import ImageFont
from PIL import ImageTk

file_types = [("JPEG (*.jpg)", "*.jpg"), ("All files (*.*)", "*.*")]
tmp_file = tempfile.NamedTemporaryFile(suffix=".jpg").name


def get_value(key, values):
    value = values[key]
    if value:
        return int(value)
    return 0


def apply_text(values, window):
    image_file = values["filename"]
    font_name = values["ttf"]
    font_size = get_value("font_size", values)
    color = values["colors"]
    x, y = get_value("text-x", values), get_value("text-y", values)
    text = values["text"]

    if image_file and os.path.exists(image_file):
        shutil.copy(image_file, tmp_file)
        image = Image.open(tmp_file)
        image.thumbnail((400, 400))

        if text:
            draw = ImageDraw.Draw(image)
            if font_name == "Default Font":
                font = None
            else:
                font = ImageFont.truetype(font_name, size=font_size)
            draw.text((x, y), text=text, font=font, fill=color)
            image.save(tmp_file)

        photo_img = ImageTk.PhotoImage(image)
        window["image"].update(data=photo_img)


def create_row(label, key, file_types):
    return [
        sg.Text(label),
        sg.Input(size=(25, 1), enable_events=True, key=key),
        sg.FileBrowse(file_types=file_types),
    ]


def get_ttf_files(directory=None):
    if directory is not None:
        ttf_files = glob.glob(directory + "/*.ttf")
    else:
        ttf_files = glob.glob("*.ttf")
    if not ttf_files:
        return {"Default Font": None}
    ttf_dict = {}
    for ttf in ttf_files:
        ttf_dict[os.path.basename(ttf)] = ttf
    return ttf_dict


def save_image(values):
    save_filename = sg.popup_get_file(
        "File", file_types=file_types, save_as=True, no_window=True
    )
    if save_filename == values["filename"]:
        sg.popup_error(
            "You are not allowed to overwrite the original image!")
    else:
        if save_filename:
            shutil.copy(tmp_file, save_filename)
            sg.popup(f"Saved: {save_filename}")


def update_ttf_values(window):
    directory = sg.popup_get_folder("Get TTF Directory")
    if directory is not None:
        ttf_files = get_ttf_files(directory)
        new_values = list(ttf_files.keys())
        window["ttf"].update(values=new_values,
                             value=new_values[0])


def main():
    colors = list(ImageColor.colormap.keys())
    ttf_files = get_ttf_files()
    ttf_filenames = list(ttf_files.keys())

    menu_items = [["File", ["Open Font Directory"]]]

    elements = [
        [sg.Menu(menu_items)],
        [sg.Image(key="image")],
        create_row("Image File:", "filename", file_types),
        [sg.Text("Text:"), sg.Input(key="text", enable_events=True)],
        [
            sg.Text("Text Position"),
            sg.Text("X:"),
            sg.Input("10", size=(5, 1), enable_events=True,
                     key="text-x"),
            sg.Text("Y:"),
            sg.Input("10", size=(5, 1), enable_events=True,
                     key="text-y"),
        ],
        [
            sg.Combo(colors, default_value=colors[0], key='colors',
                     enable_events=True),
            sg.Combo(ttf_filenames, default_value=ttf_filenames[0], key='ttf',
                     enable_events=True),
            sg.Text("Font Size:"),
            sg.Input("12", size=(5, 1), key="font_size", enable_events=True),

        ],
        [sg.Button("Save Image", enable_events=True,
                   key="save")],
    ]

    window = sg.Window("Draw Text GUI", elements)

    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event in ["filename", "colors", "ttf", "font_size",
                     "text-x", "text-y", "text"]:
            apply_text(values, window)
        if event == "save" and values["filename"]:
            save_image(values)
        if event == "Open Font Directory":
            update_ttf_values(window)

    window.close()

if __name__ == "__main__":
    main()

В этом GUI находятся более 100 строк кода! Чтобы облегчить понять, что происходит, вы перейдете к коду в меньших кусках.

Вот первые несколько строк кода:

# text_gui.py

import glob
import os
import PySimpleGUI as sg
import shutil
import tempfile

from PIL import Image
from PIL import ImageColor
from PIL import ImageDraw
from PIL import ImageFont
from PIL import ImageTk

file_types = [("JPEG (*.jpg)", "*.jpg"), ("All files (*.*)", "*.*")]
tmp_file = tempfile.NamedTemporaryFile(suffix=".jpg").name

Это импортный раздел вашего кода. Вы импортируете все необходимые модули и пакеты, которые необходимо сделать свой код. Вы также устанавливаете пару переменных там в конце. file_types Определяет, какие типы файлов могут загружаться ваш пользовательский интерфейс. tmp_file это временный файл, который вы создаете, чтобы сохранить ваши изменения изображения до тех пор, пока пользователь не будет готово сохранить файл, где они этого хотят.

Первая функция в вашем коде называется get_value () Отказ Вот его код:

def get_value(key, values):
    value = values[key]
    if value:
        return int(value)
    return 0

Эта задача этой функции состоит в том, чтобы преобразовать строки в целые числа и возвращать ноль, если пользователь опустошает элемент управления, который требует значения.

Теперь вы можете перейти, чтобы узнать о apply_text () :

def apply_text(values, window):
    image_file = values["filename"]
    font_name = values["ttf"]
    font_size = get_value("font_size", values)
    color = values["colors"]
    x, y = get_value("text-x", values), get_value("text-y", values)
    text = values["text"]

    if image_file and os.path.exists(image_file):
        shutil.copy(image_file, tmp_file)
        image = Image.open(tmp_file)
        image.thumbnail((400, 400))

        if text:
            draw = ImageDraw.Draw(image)
            font = ImageFont.truetype(font_name, size=font_size)
            draw.text((x, y), text=text, font=font, fill=color)
            image.save(tmp_file)

        photo_img = ImageTk.PhotoImage(image)
        window["image"].update(data=photo_img)

Вот где происходит волшебство. Этот код работает всякий раз, когда пользователь открывает новый образ, меняет шрифт, изменяет размер шрифта, измените цвет шрифта, изменяют сам текст или изменяет положение текста. Если пользователь не вводил ни один текст, но они выбрали изображение, только изображение изменится. Если пользователь ввел текст, то другие изменения также будут обновлены.

Примечание. Когда изображение сохраняется, он сохраняется как версия миниатюр. Таким образом, вы будете спасать меньшую версию изображения, но с текстом на нем.

Следующая функция, на которой вы смотрите, называется create_row () :

def create_row(label, key, file_types):
    return [
        sg.Text(label),
        sg.Input(size=(25, 1), enable_events=True, key=key),
        sg.FileBrowse(file_types=file_types),
    ]

Вы видели эту функцию раньше. Используется для создания трех элементов:

  • Этикетка ( sg.text )
  • Текстовое поле ( sg.input )
  • Кнопка просмотра файлов ( sg.filebrowse )

Эти элементы возвращаются в списке Python. Это создаст горизонтальную строку элементов в вашем пользовательском интерфейсе.

Следующая функция, чтобы узнать о том get_ttf_files () :

def get_ttf_files(directory=None):
    if directory is not None:
        ttf_files = glob.glob(directory + "/*.ttf")
    else:
        ttf_files = glob.glob("*.ttf")
    if not ttf_files:
        return {"Default Font": None}
    ttf_dict = {}
    for ttf in ttf_files:
        ttf_dict[os.path.basename(ttf)] = ttf
    return ttf_dict

Этот код использует Python’s шаблон Модуль Чтобы найти все файлы шрифтов TrueType в каталоге, в котором файл GUI находится в или ищет каталог что вы прошли. Если графический интерфейс не находит какие-либо файлы TTF, он будет загружать шрифт по умолчанию по умолчанию.

Независимо от того, что произойдет, ваш код вернет словарь Python, который отображает имя шрифта в абсолютный путь к файлу шрифта. В случае, если ваш код не находит никаких файлов TTF, он будет отображаться «шрифт по умолчанию» на Нет Чтобы указать свой код, который не было обнаружено никаких шрифтов, поэтому вы будете использовать шрифт по умолчанию по умолчанию.

Следующая функция определяет, как сохранить отредактированное изображение:

def save_image(values):
    save_filename = sg.popup_get_file(
        "File", file_types=file_types, save_as=True, no_window=True
    )
    if save_filename == values["filename"]:
        sg.popup_error(
            "You are not allowed to overwrite the original image!")
    else:
        if save_filename:
            shutil.copy(tmp_file, save_filename)
            sg.popup(f"Saved: {save_filename}")

Этот код просит пользователя, что назвать их новое изображение с помощью всплывающего диалогового окна. Затем он проверяет, чтобы пользователь не перезаписал их исходное изображение. На самом деле, вы не мешаете, что здесь происходит, показывая им сообщение об ошибке, если они пытаются это сделать.

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

Теперь вы готовы узнать о update_ttf_values () Функция:

def update_ttf_values(window):
    directory = sg.popup_get_folder("Get TTF Directory")
    if directory is not None:
        ttf_files = get_ttf_files(directory)
        new_values = list(ttf_files.keys())
        window["ttf"].update(values=new_values,
                             value=new_values[0])

Эта функция называется из Файл Меню в вашем пользовательском интерфейсе. Когда он призван, он покажет диалог, спрашивающий пользователя, где расположены их файлы TTF. Если пользователь нажимает Отмена , каталог не возвращается. Но если пользователь выбирает каталог и принимает диалог, то вы звоните gett_ttf_files () И он будет искать какие-либо файлы TTF, которые находятся в папке.

Затем он будет обновлять содержимое вашего Combobox TTF, чтобы вы могли выбрать шрифт TTF, чтобы применить к вашему тексту.

Последняя функция, которую вам нужно просмотреть, это Главная () один:

def main():
    colors = list(ImageColor.colormap.keys())
    ttf_files = get_ttf_files()
    ttf_filenames = list(ttf_files.keys())

    menu_items = [["File", ["Open Font Directory"]]]

    elements = [
        [sg.Menu(menu_items)],
        [sg.Image(key="image")],
        create_row("Image File:", "filename", file_types),
        [sg.Text("Text:"), sg.Input(key="text", enable_events=True)],
        [
            sg.Text("Text Position"),
            sg.Text("X:"),
            sg.Input("10", size=(5, 1), enable_events=True,
                     key="text-x"),
            sg.Text("Y:"),
            sg.Input("10", size=(5, 1), enable_events=True,
                     key="text-y"),
        ],
        [
            sg.Combo(colors, default_value=colors[0], key='colors',
                     enable_events=True),
            sg.Combo(ttf_filenames, default_value=ttf_filenames[0], key='ttf',
                     enable_events=True),
            sg.Text("Font Size:"),
            sg.Input("12", size=(5, 1), key="font_size", enable_events=True),

        ],
        [sg.Button("Save Image", enable_events=True,
                   key="save")],
    ]

    window = sg.Window("Draw Text GUI", elements)

Первые три строки в этой функции создают список Python Цвета и список ttf_filenames Отказ Далее вы создаете вложенный список, который представляет ваше меню для вашего пользовательского интерфейса. Тогда вы создаете Элементы Список, который вы используете для размещения всех элементов, которые используются для создания вашего пользовательского интерфейса. Как только у вас есть, вы можете передать его на ваш SG.Window , который будет макет ваших элементов и покажет их пользователю.

Последний кусок кода – обработчик событий вашего пользователя пользователя:

    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event in ["filename", "colors", "ttf", "font_size",
                     "text-x", "text-y", "text"]:
            apply_text(values, window)
        if event == "save" and values["filename"]:
            save_image(values)
        if event == "Open Font Directory":
            update_ttf_values(window)

    window.close()

if __name__ == "__main__":
    main()

Здесь вы проверяете, закрыл ли пользователь пользователя окно. Если они сделали, вы выходите из цикла и завершите программу.

Следующий условный оператор проверяет все остальные события, кроме «Сохранения» и события меню. Если любой из других элементов, которые имеют включенные события, срабатывают, вы запускаете apply_text () Отказ Это обновляет изображение в соответствии с настройками (ыми), которые вы меняете.

Следующий условный сохраняет изображение, когда вы закончите редактирование его. Это позвонит Save_image () Что попросит пользователю, куда они хотят сохранить образ.

Последний условный для вашего Файл меню событие. Если пользователь выбирает опцию «Каталог открытых шрифтов», оно закончится вызов update_ttf_values () , который позволяет выбрать новую папку TTF для поиска.

Обертывание

Вы создали хороший графический интерфейс, который вы можете использовать, чтобы узнать, как нарисовать текст на собственных изображениях. Этот код учит вас, как эффективно использовать подушку для рисования текста. Он также учит вам, как создать функциональный графический интерфейс пользователя с помощью Pysimplegui. Этот GUI работает на Windows, Mac и Linux. Дайте это попробуйте и добавьте несколько классных новых функций, чтобы сделать его собственным.