Создание графического интерфейса с нуля: Пошаговое руководство

Вам кажется интересным программирование графических интерфейсов? Если да, то давайте создадим его с нуля!

Это руководство поможет вам приступить к созданию GUI-приложений на Python с помощью Tkinter. Существует множество фреймворков для создания графических интерфейсов, но мы будем использовать Tkinter просто потому, что он прост в использовании, а синтаксис остается неизменным независимо от того, используете ли вы Windows, Mac или Ubuntu.

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

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, colorchooser
from PIL import Image, ImageTk, ImageFilter, ImageEnhance

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

root = tk.Tk() # To create a window
root.geometry("1100x600")
root.title("Photo Editor")
root.mainloop() # To hold the window

Можно создать несколько окон и пожелать для каждого из них свои виджеты. Чтобы определить, в каком окне какие виджеты должны быть, мы пишем код между этими двумя строками – объявлением окна и .mainloop().

Здесь я определил размер окна с помощью root.geometry() и добавил заголовок с помощью root.title(). При выполнении приведенного выше кода должно появиться пустое окно, как показано ниже:

Создание графического интерфейса с нуля: Пошаговое руководство
Обратите внимание, что в левой верхней части отображается заголовок нашего окна

Теперь, когда экран нашего окна готов, можно приступить к добавлению на него виджетов. В Tkinter для размещения любого виджета можно использовать .pack(), .place() или .grid().

.pack() – размещает виджеты в одной из 4 доступных позиций, т.е. сверху (по умолчанию), снизу, слева или справа

.place() – размещает виджеты с использованием координат x, y

.grid() – размещает виджеты с использованием координат строк и столбцов.

Давайте разберемся, что такое кнопки, метки, входы и комбобоксы.

ttk.Button() – создает щелкающую кнопку, которая может выполнять определенное действие

ttk.Label() – создает метку, которая может отображать текст или изображение

ttk.Entry() – позволяет пользователю вводить информацию, которая может представлять собой текст или цифры

ttk.Combobox() – используется для отображения списка вариантов для выбора (по сути, выпадающий список).

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

root = tk.Tk()
root.geometry("1150x600")
root.title("Photo Editor")

ttk.Button(root, text="Open Image").grid(row=0, column=0, padx=10, pady=10)

ttk.Label(root, text="Filter:").grid(row=0, column=1, padx=10, pady=10)
filter_options = ["Black and White", "Sepia"]
ttk.Combobox(root, values=filter_options).grid(row=0, column=2, padx=10, pady=10)

ttk.Button(root, text="Apply Filter").grid(row=0, column=3, padx=10, pady=10)

ttk.Label(root, text="Border Size:").grid(row=0, column=4, padx=10, pady=10)
ttk.Entry(root).grid(row=0, column=5, padx=10, pady=10)

ttk.Label(root, text="Border Color:").grid(row=0, column=6, padx=10, pady=10)
ttk.Button(root, text="Pick Color").grid(row=0, column=7, padx=10, pady=10)

ttk.Button(root, text="Add Border").grid(row=0, column=8, padx=10, pady=10)

ttk.Button(root, text="Clear All Filters").grid(row=0, column=9, padx=10, pady=10)

ttk.Button(root, text="Save Image").grid(row=0, column=10, padx=10, pady=10)

root.mainloop()

Я использовал ttk.Combobox(), который позволяет пользователю выбрать фильтр для применения, и ttk.Entry(), позволяющий пользователю определить размер границы, которую он хочет применить.

Далее вы можете заметить, что .grid() позволяет легко разместить ваши виджеты, а padding позволяет добавить пространство между ними, посмотрите на следующие изображения!

Создание графического интерфейса с нуля: Пошаговое руководство
Создание графического интерфейса с нуля: Пошаговое руководство
Левое изображение (с разметкой) и правое изображение (без разметки)

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

filter_var = tk.StringVar()
border_size = tk.IntVar()
border_color = tk.StringVar()

original_image = None
edited_image = None

def process_image(root, filter_var, border_size, border_color):
    global original_image, edited_image
    file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif")])

    if file_path:
        image_path = file_path
        original_image = Image.open(image_path)
        edited_image = original_image.copy()
        display_image(edited_image)

Сначала мы откроем изображение, для этого используется filedialog.askopenfilename(), затем сделаем копию исходного изображения и сохраним ее как отредактированное изображение (не волнуйтесь, мы будем постоянно обновлять это изображение!)

def apply_filter():
    global edited_image
    selected_filter = filter_var.get()

    if edited_image and selected_filter == "Black and White":
        edited_image = ImageEnhance.Color(edited_image).enhance(0.0)
    elif edited_image and selected_filter == "Sepia":
        edited_image = apply_sepia_filter(edited_image)

    display_image(edited_image)

def apply_sepia_filter(image):
    sepia = Image.new('RGB', image.size, (112, 66, 20))
    sepia = Image.blend(image, sepia, 0.5)
    return sepia

Теперь приступим к применению фильтров, пока что у нас будет только два фильтра – “Черно-белый” и “Сепия”.

Функция .enhance() в apply_filter() используется для улучшения изображения, здесь мы установили ее значение 0.0, что означает отсутствие улучшения цвета изображения, в результате чего оно становится черно-белым и сохраняется в edited_image Для применения фильтра сепии я определил другую функцию и назначил ей RGB-триплет, т.е. (112, 66, 20).

border_size.set(20) # Default border size: 20
border_color.set("#000000")  # Default border color: Black

def add_border():
    global edited_image
    if edited_image:
        size = border_size.get()
        color = border_color.get()

        # Adding a border
        new_width = edited_image.width + 2 * size
        new_height = edited_image.height + 2 * size

        bordered_image = Image.new("RGB", (new_width, new_height), color)
        bordered_image.paste(edited_image, (size, size))

        edited_image = bordered_image
        display_image(edited_image)

def pick_border_color():
    color = colorchooser.askcolor(title="Pick a Border Color", initialcolor=border_color.get())[1]
    if color:
        border_color.set(color)

Далее я добавил границы к изображению. По умолчанию размер границы равен 20, а цвет – черный. Но что, если вам не нужен черный цвет? Да, вы можете изменить его. Я использовал функцию colorchooser.askcolor(), поэтому при нажатии на кнопку “Выбрать цвет” появляется окно выбора цвета, и вы можете выбрать цвет по своему усмотрению!

def clear_filters():
    global original_image, edited_image
    edited_image = original_image.copy()
    display_image(edited_image)

def save_image():
    global edited_image
    save_path = filedialog.asksaveasfilename(defaultextension=".png",
                                               filetypes=[("PNG files", "*.png"),
                                                          ("JPEG files", "*.jpg;*.jpeg")])
    if save_path:
        edited_image.save(save_path)

Не довольны правкой? Я добавил кнопку “Очистить фильтр”, которая удалит все сделанные до этого правки и восстановит исходное изображение. Наконец, добавлена кнопка “Сохранить изображение”, которая сохранит отредактированное изображение в системе!

def display_image(edited_image):
    global canvas
    if edited_image:
        # Resizing image to fit the canvas
        resized_image = edited_image.resize((500, 500), Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(resized_image)

        # Updating canvas with the new image
        canvas.config(width=photo.width(), height=photo.height())
        canvas.create_image(0, 0, anchor=tk.NW, image=photo)
        canvas.image = photo

Подождите, а где мы все это увидим? Ответ – на холсте, функция display_image() помогает вывести изображение на холст.

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

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, colorchooser
from PIL import Image, ImageTk, ImageFilter, ImageEnhance

def create_widgets(root, filter_var, border_size, border_color):
    ttk.Button(root, text="Open Image", command=lambda: process_image(root, filter_var, border_size, border_color)).grid(row=0, column=0, padx=10, pady=10)
    ttk.Label(root, text="Filter:").grid(row=0, column=1, padx=10, pady=10)
    filter_options = ["Black and White", "Sepia"]
    ttk.Combobox(root, textvariable=filter_var, values=filter_options).grid(row=0, column=2, padx=10, pady=10)
    ttk.Button(root, text="Apply Filter", command=apply_filter).grid(row=0, column=3, padx=10, pady=10)
    ttk.Label(root, text="Border Size:").grid(row=0, column=4, padx=10, pady=10)
    ttk.Entry(root, textvariable=border_size).grid(row=0, column=5, padx=10, pady=10)
    ttk.Label(root, text="Border Color:").grid(row=0, column=6, padx=10, pady=10)
    ttk.Button(root, text="Pick Color", command=pick_border_color).grid(row=0, column=7, padx=10, pady=10)
    ttk.Button(root, text="Add Border", command=add_border).grid(row=0, column=8, padx=10, pady=10)
    ttk.Button(root, text="Clear All Filters", command=clear_filters).grid(row=0, column=9, padx=10, pady=10)
    ttk.Button(root, text="Save Image", command=save_image).grid(row=0, column=10, padx=10, pady=10)

    canvas = tk.Canvas(root, width=500, height=500)
    canvas.grid(row=1, column=0, columnspan=11, padx=10, pady=10)

    for i in range(11):
        root.columnconfigure(i, weight=1)
    root.rowconfigure(1, weight=1)

    return canvas

def process_image(root, filter_var, border_size, border_color):
    global original_image, edited_image
    file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif")])

    if file_path:
        image_path = file_path
        original_image = Image.open(image_path)
        edited_image = original_image.copy()
        display_image(edited_image)

def apply_filter():
    global edited_image
    selected_filter = filter_var.get()

    if edited_image and selected_filter == "Black and White":
        edited_image = ImageEnhance.Color(edited_image).enhance(0.0)
    elif edited_image and selected_filter == "Sepia":
        edited_image = apply_sepia_filter(edited_image)

    display_image(edited_image)

def apply_sepia_filter(image):
    sepia = Image.new('RGB', image.size, (112, 66, 20))
    sepia = Image.blend(image, sepia, 0.5)
    return sepia

def add_border():
    global edited_image
    if edited_image:
        size = border_size.get()
        color = border_color.get()

        # Adding a border
        new_width = edited_image.width + 2 * size
        new_height = edited_image.height + 2 * size

        bordered_image = Image.new("RGB", (new_width, new_height), color)
        bordered_image.paste(edited_image, (size, size))

        edited_image = bordered_image
        display_image(edited_image)

def pick_border_color():
    color = colorchooser.askcolor(title="Pick a Border Color", initialcolor=border_color.get())[1]
    if color:
        border_color.set(color)

def clear_filters():
    global original_image, edited_image
    edited_image = original_image.copy()
    display_image(edited_image)

def save_image():
    global edited_image
    save_path = filedialog.asksaveasfilename(defaultextension=".png",
                                               filetypes=[("PNG files", "*.png"),
                                                          ("JPEG files", "*.jpg;*.jpeg")])
    if save_path:
        edited_image.save(save_path)

def display_image(edited_image):
    global canvas
    if edited_image:
        # Resizing image to fit the canvas
        resized_image = edited_image.resize((500, 500), Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(resized_image)

        # Updating canvas with the new image
        canvas.config(width=photo.width(), height=photo.height())
        canvas.create_image(0, 0, anchor=tk.NW, image=photo)
        canvas.image = photo

root = tk.Tk()
root.geometry("1100x600")
root.title("Photo Editor")

filter_var = tk.StringVar()
border_size = tk.IntVar()
border_color = tk.StringVar()
border_size.set(20)  # Default border size: 20
border_color.set("#000000")  # Default border color: Black

canvas = create_widgets(root, filter_var, border_size, border_color)

original_image = None
edited_image = None

root.mainloop()
Создание графического интерфейса с нуля: Пошаговое руководство
Функциональный GUI

Вот так я создал этот очень простой фоторедактор на python 🙂

Надеюсь, это помогло вам познакомиться с графическим интерфейсом!

Спасибо за чтение!

+1
2
+1
1
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *