Создание графического интерфейса с нуля: Пошаговое руководство
Вам кажется интересным программирование графических интерфейсов? Если да, то давайте создадим его с нуля!
Это руководство поможет вам приступить к созданию 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()
Вот так я создал этот очень простой фоторедактор на python 🙂
Надеюсь, это помогло вам познакомиться с графическим интерфейсом!
Спасибо за чтение!