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

Pysimplegui – как рисовать фигуры на изображении с графическим интерфейсом

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

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

Рисование фигур на изображениях аккуратно. Но не было бы неплохо, если бы вы могли использовать формы интерактивно? Это точка этого учебника. Вы создадите пользовательский интерфейс, используя Pysimplegui Чтобы позволить вам рисовать фигуры на изображениях!

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

  • эллипс
  • прямоугольник

Причина этого состоит в том, что эти две формы принимают те же аргументы. Вы можете взять на себя вызов самостоятельно, чтобы добавить другие формы для GUI!

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

Начиная

Вам понадобится Pysimplegui и подушка, чтобы иметь возможность следовать вместе с этим руководством. Вот как установить их с PIP:

python3 -m pip install PySimpleGUI pillow

Теперь вы готовы создать ваш графический интерфейс!

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

Теперь откройте свой редактор Python и создайте новый файл с именем Drawing_gui.py Отказ Затем добавьте этот код в свой новый файл:

# drawing_gui.py

import io
import os
import PySimpleGUI as sg
import shutil
import tempfile

from PIL import Image, ImageColor, ImageDraw

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


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


def apply_drawing(values, window):
    image_file = values["-FILENAME-"]
    shape = values["-SHAPES-"]
    begin_x = get_value("-BEGIN_X-", values)
    begin_y = get_value("-BEGIN_Y-", values)
    end_x = get_value("-END_X-", values)
    end_y = get_value("-END_Y-", values)
    width = get_value("-WIDTH-", values)
    fill_color = values["-FILL_COLOR-"]
    outline_color = values["-OUTLINE_COLOR-"]

    if os.path.exists(image_file):
        shutil.copy(image_file, tmp_file)
        image = Image.open(tmp_file)
        image.thumbnail((400, 400))
        draw = ImageDraw.Draw(image)
        if shape == "Ellipse":
            draw.ellipse(
                (begin_x, begin_y, end_x, end_y),
                fill=fill_color,
                width=width,
                outline=outline_color,
            )
        elif shape == "Rectangle":
            draw.rectangle(
                (begin_x, begin_y, end_x, end_y),
                fill=fill_color,
                width=width,
                outline=outline_color,
            )
        image.save(tmp_file)

        bio = io.BytesIO()
        image.save(bio, format="PNG")
        window["-IMAGE-"].update(data=bio.getvalue())


def create_coords_elements(label, begin_x, begin_y, key1, key2):
    return [
        sg.Text(label),
        sg.Input(begin_x, size=(5, 1), key=key1, enable_events=True),
        sg.Input(begin_y, size=(5, 1), key=key2, enable_events=True),
    ]


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 main():
    colors = list(ImageColor.colormap.keys())
    layout = [
        [sg.Image(key="-IMAGE-")],
        [
            sg.Text("Image File"),
            sg.Input(
                size=(25, 1), key="-FILENAME-"
            ),
            sg.FileBrowse(file_types=file_types),
            sg.Button("Load Image"),
        ],
        [
            sg.Text("Shapes"),
            sg.Combo(
                ["Ellipse", "Rectangle"],
                default_value="Ellipse",
                key="-SHAPES-",
                enable_events=True,
                readonly=True,
            ),
        ],
        [
            *create_coords_elements(
                "Begin Coords", "10", "10", "-BEGIN_X-", "-BEGIN_Y-"
            ),
            *create_coords_elements(
                "End Coords", "100", "100", "-END_X-", "-END_Y-"
            ),
        ],
        [
            sg.Text("Fill"),
            sg.Combo(
                colors,
                default_value=colors[0],
                key="-FILL_COLOR-",
                enable_events=True,
                readonly=True
            ),
            sg.Text("Outline"),
            sg.Combo(
                colors,
                default_value=colors[0],
                key="-OUTLINE_COLOR-",
                enable_events=True,
                readonly=True
            ),
            sg.Text("Width"),
            sg.Input("3", size=(5, 1), key="-WIDTH-", enable_events=True),
        ],
        [sg.Button("Save")],
    ]

    window = sg.Window("Drawing GUI", layout, size=(450, 500))

    events = [
        "Load Image",
        "-BEGIN_X-",
        "-BEGIN_Y-",
        "-END_X-",
        "-END_Y-",
        "-FILL_COLOR-",
        "-OUTLINE_COLOR-",
        "-WIDTH-",
        "-SHAPES-",
    ]
    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event in events:
            apply_drawing(values, window)
        if event == "Save" and values["-FILENAME-"]:
            save_image(values)
    window.close()


if __name__ == "__main__":
    main()

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

Первый кусок – код в верхней части файла:

# drawing_gui.py

import io
import os
import PySimpleGUI as sg
import shutil
import tempfile

from PIL import Image, ImageColor, ImageDraw

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

Эти строки кода определяют импорт пакетов и модулей, которые вам нужны. Это также устанавливает две переменные:

  • file_types – который вы будете использовать для просмотра и сохранения ваших изображений
  • tmp_file – временный файл, созданный для сохранения вашего промежуточного файла изображения

Теперь вы можете взглянуть на вашу первую функцию:

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

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

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

Следующая функция – это мясистый:

def apply_drawing(values, window):
    image_file = values["-FILENAME-"]
    shape = values["-SHAPES-"]
    begin_x = get_value("-BEGIN_X-", values)
    begin_y = get_value("-BEGIN_Y-", values)
    end_x = get_value("-END_X-", values)
    end_y = get_value("-END_Y-", values)
    width = get_value("-WIDTH-", values)
    fill_color = values["-FILL_COLOR-"]
    outline_color = values["-OUTLINE_COLOR-"]

    if image_file and os.path.exists(image_file):
        shutil.copy(image_file, tmp_file)
        image = Image.open(tmp_file)
        image.thumbnail((400, 400))
        draw = ImageDraw.Draw(image)
        if shape == "Ellipse":
            draw.ellipse(
                (begin_x, begin_y, end_x, end_y),
                fill=fill_color,
                width=width,
                outline=outline_color,
            )
        elif shape == "Rectangle":
            draw.rectangle(
                (begin_x, begin_y, end_x, end_y),
                fill=fill_color,
                width=width,
                outline=outline_color,
            )
        image.save(tmp_file)

        bio = io.BytesIO()
        image.save(bio, format="PNG")
        window["-IMAGE-"].update(data=bio.getvalue())

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

  • Имя изображения
  • Форма Combobox.
  • Начальные координаты ( -Begin_x- , -Begin_y- )
  • Координаты координат ( -end_x- , -end_y- )
  • Ширина наброска
  • Цвет Fill для использования (использует цветные имена)
  • Цветной цвет (использует цветные имена)

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

Примечание. Чертеж наносится на эскизную версию изображения, а не копию изображения. Это сделано, чтобы сделать изображение видимым в вашем графическом интерфейсе. Многие фотографии имеют более высокое разрешение, чем можно показать на типичном мониторе.

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

def create_coords_elements(label, begin_x, begin_y, key1, key2):
    return [
        sg.Text(label),
        sg.Input(begin_x, size=(5, 1), key=key1, enable_events=True),
        sg.Input(begin_y, size=(5, 1), key=key2, enable_events=True),
    ]

Эта функция возвращает список Python, который содержит три элемента в нем. Один лейбл ( sg.text ) и два текстовых поля ( sg.input ). Поскольку эти элементы находятся в одном списке, они будут добавлены в виде горизонтальной строки к вашему пользовательскому интерфейсу.

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

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 main():
    colors = list(ImageColor.colormap.keys())
    layout = [
        [sg.Image(key="-IMAGE-")],
        [
            sg.Text("Image File"),
            sg.Input(
                size=(25, 1), key="-FILENAME-"
            ),
            sg.FileBrowse(file_types=file_types),
            sg.Button("Load Image"),
        ],
        [
            sg.Text("Shapes"),
            sg.Combo(
                ["Ellipse", "Rectangle"],
                default_value="Ellipse",
                key="-SHAPES-",
                enable_events=True,
                readonly=True,
            ),
        ],
        [
            *create_coords_elements(
                "Begin Coords", "10", "10", "-BEGIN_X-", "-BEGIN_Y-"
            ),
            *create_coords_elements(
                "End Coords", "100", "100", "-END_X-", "-END_Y-"
            ),
        ],
        [
            sg.Text("Fill"),
            sg.Combo(
                colors,
                default_value=colors[0],
                key="-FILL_COLOR-",
                enable_events=True,
                readonly=True
            ),
            sg.Text("Outline"),
            sg.Combo(
                colors,
                default_value=colors[0],
                key="-OUTLINE_COLOR-",
                enable_events=True,
                readonly=True
            ),
            sg.Text("Width"),
            sg.Input("3", size=(5, 1), key="-WIDTH-", enable_events=True),
        ],
        [sg.Button("Save")],
    ]

    window = sg.Window("Drawing GUI", layout, size=(450, 500))

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

  • * create_Coords_Elements ()

Когда вы звоните create_coords_Elements () он возвращает список. Но вы хотите как начало, так и заканчивая координаты на одной линии. Итак, вы извлекаете элементы из списка.

Этот маленький пример кода иллюстрирует то, что происходит:

>>> def get_elements():
...     return ["element_one", "element_two"]
... 
>>> [*get_elements()]
['element_one', 'element_two']

Если вы не используете звездочку, вы получите вложенный список вместо списка, который в нем есть три элемента.

Вот последние несколько строк кода из Главная () Функция:

    events = [
        "Load Image",
        "-BEGIN_X-",
        "-BEGIN_Y-",
        "-END_X-",
        "-END_Y-",
        "-FILL_COLOR-",
        "-OUTLINE_COLOR-",
        "-WIDTH-",
        "-SHAPES-",
    ]
    while True:
        event, values = window.read()
        if event == "Exit" or event == sg.WIN_CLOSED:
            break
        if event in events:
            apply_drawing(values, window)
        if event == "Save" and values["-FILENAME-"]:
            save_image(values)
    window.close()

if __name__ == "__main__":
    main()

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

Обертывание

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

Попробуйте посмотрите, что вы можете придумать!

Связанное чтение

  • Рисование фигур на изображениях с питоном и подушкой
  • Рисование текста на изображениях с питоном и подушкой
  • Pysimplegui: Рисование текста на изображениях с Python GUI
Подушка: обработка изображений с покупкой Python сейчас на leancub