Содержание статьи
Основы разработки и проектирования интерфейса
Обычно такие статьи начинаются с банальных вопросов по типу «Что такое интерфейс», «Что такое разработка», «Что такое проектирование». Суть в том, что это не так важно в рамках данной статьи, и к тому же без того интуитивно понятно. Интуитивно понятно также, как и должен быть понятен хороший пользовательский интерфейс!
Из экономики можно подчерпнуть то, что существует три типа реализации продукта/модели взаимодействия на рынке: от бизнеса к бизнесу, от бизнеса к потребителю и от потребителя к потребителю (последнее – допустим, доски объявлений, тот же Авито). По части ПО мы имеем нечто похожее: от программиста к программисту, от программиста к пользователю, от пользователя к пользователю. Например, когда одни программисты создают библиотеку или новый усовершенствованный ЯП – они нацелены прежде всего на аудиторию программистов. А если идёт разработка ПО для создания и редактирования графических объектов (тот же Photoshop) – то главной аудиторией является пользователь в самом широком смысле этого слова. От пользователя к пользователю же – это когда один юзер отправляет другому программу на Visual Basic для анализа чего бы то ни было в Excel (либо других таблицах).
Проблема какая, прежде всего: мы любим, когда всё видно, существует логика, последовательность, в общем – когда ПО напоминает больше музыкальный инструмент, а не набор функций (последнее примемлемо только для библиотек Python – но даже они соревнуются в понятности между собой). В общем, никому не нужен инструмент, которым непонятно, как пользоваться. И если в связке программист-программисту это не так страшно (хотя неприятно, те же IDE штука полезная), то создание ПО для массового пользователя без интерфейса – это означает создание ПО, которое не откроют никогда. Ваш автомобиль может быть шикарен в большинстве своём, но если человек не понимает, как открыть дверь и где там вообще руль – это провал. А если вы вдобавок выдадите ему вместо объяснений 200-страничный документ – провал вдвойне, опять же, если речь про классического потребителя.
Следовательно, чтобы создать хороший продукт, нужны три вещи: хороший функционал, надёжная структура и исчерпывающий UI. Чтобы посмотреть на окно программы было достаточно, чтобы понять, что можно в ней реализовывать. Зачем я именно это так подробно поясняю? Да потому что можно сколько угодно учить, какие объекты есть возможность расстановить с помощью того или иного инструмента, но навык понимания структуры расстановки от этого не прокачается.
Данная статья – не совсем образовательная, поскольку всевозможные подробности можно посмотреть в сети. Это будет классический roadmap – что и зачем учить, чтобы понять азы разработки пользовательского интерфейса и в ближайшее время начать делать ПО, которым пользуются.
Так как Python сегодня пойдёт на любом чайнике, то предлагаю освоить фундамент всех фундаментов – Tkinter. На самом деле когда я впервые рассматривал основной функционал этой библиотеки, меня не покидала мысль, что это… язык разметки, встроенный прямо в Питон. Да, как BBcode, HTML, MarkDown, впрочем, про это ещё будет своя статья.
Есть два типа элементов в Tkinter’е: виджеты и методы размещения. Первое – классические объекты. Если вы скачаете какое-нибудь приложение по изучению английского языка на свой телефон, то в разделе «Виджеты» на вашем телефоне точно появится возможность добавления чего-то эдакого на свой экран. Тут то же самое: добавляем «чего-то эдакого». Но как добавлять? Здесь уже интереснее, и там поможет нам второй тип элементов – методы размещения.
Но разберёмся с объектами. Основные виды: три типа кнопок (Button – обычная, Checkbutton – флаг, Radiobutton – переключатель), три типа текстовых объектов (Label – текстовая метка, Entry – одна строка, Text – много строк) и три вспомогательных (Frame – контейнер, Listbox – список, Scrollbar – полоса прокрутки).
Простые и понятные примеры использования приведены ниже.
Кнопки:
tk.Button(root, text=»Нажми меня», command=on_click)
tk.Checkbutton(root, text=»Согласен», variable=var1)
tk.Radiobutton(root, text=»Вариант A», variable=choice, value=»A»)
Текст:
tk.Label(root, text=»Привет, мир!», font=(«Arial», 16), fg=»blue»)
tk.Entry(root, width=25)
tk.Text(root, height=5, width=30)
Списки:
tk.Frame(root, bg=»lightblue», width=200, height=50)
tk.Listbox(root, height=5)
tk.Scrollbar(root, orient=»vertical»)
И разумеется методы размещения! Без этих методов, указанных после создания объектов, они не будут видны. Нужно указать один из трёх:
pack() – это автоматическое размещение виджетов, grid() – размещение виджетов в таблице, и place() – размещение виджетов по координатам.
Пример использования (работает со всеми объектами):
tk.Label(root, text=»Привет, мир!», font=(«Arial», 16), fg=»blue»).pack()
Создание собственных расширений
Окей, перейдём от теории к практике (что мною обожаемо ещё как). Допустим, мы породили кнопку:
tk.Button(root, text=»Запустить функцию X», command=X_function).pack()
Что такое «функция X»? Зачастую это – усовершенствованный парсер, в народе ещё называемый интерпретатором. Интерпретатор – программа, функция, которая выполняет роль «переводчика»: меняет одни символы на другие и обрабатывает контекст. Например, известно, что мы можем перевести BBcode в HTML.
Таким образом мы приходим к необходимости создания своих «языков», подразумевая под этим также создание расширений. Вообще что такое «расширение»? Расширение – это просто метка, которая говорит и пользователю, и программному обеспечению, как работать с тем или иным файлом и как его позиционировать. Для документа – один интерпретатор, для видео – другой. Следовательно необходимы две вещи: смысл интерпретаций (зачем, для кого), продуманная логика (как, почему), интерфейс (Tkinter).
Допустим нам нужна программа, которая переводит двоичный код в десятеричные числа и текст. Для кого? Предположим, для шифрования тех или иных данных (как составная часть какого-то более сложного метода шифрования). Как? Было бы неплохо нажать на кнопку, выбрать файл с расширением, предположим, «doublecode» или любое другое название (иное программа принимать не должна – будет странно загрузить в неё PNG), и получить на выходе рядышком или в какой-то папке расшифрованный текст.
Как создать кнопку понятно. Как сделать интерпретатор?
Сначала берём исходную строку/строки, в нашем случае строчка с двоичным кодом:
binary = «01001000 01100101 01101100 01101100 01101111»
Убираем пробелы с помощью «replace». Внимание: «replace» (заменить) – штука крайне полезная, следует на ней остановиться и поподробнее поизучать, потому что это буквально сердце интерпретаторов (она превращает одну совокупность символов в другую). Жаль, что в данном примере она имеет посредственную роль по сравнению с той, что выделяется этому методу обычно.
binary_string = binary.replace(» «, «»)
Далее нам нужен просто пустой «text», куда мы запишем итог.
text = «»
А потом сам интерпретатор. Проще говоря: некоторая функция, которая превращает одну комбинацию символов в другую. Здесь хотелось бы остановиться на функции «chr» – существует обратная ей функция «ord». И то и другое работает по принципу replace (заменить), просто здесь уже заданы параметры, что на что менять: «ord» возвращает его код в кодировке Unicode/ASCII, а «chr» наоборот – принимает код символа и возвращает соответствующий ему символ. Как таблица соответствий.
for i in range(0, len(binary_string), 8):
byte = binary_string[i:i+8]
text += chr(int(byte, 2))
print(text)
А теперь про «replace». Давайте сделаем замену слова «Hello» на слово «Hi».
Важно! Синтаксис: строка.replace(«что_заменить», «на_что_заменить»).
Следовательно наш перевод будет организован в одну строку:
text.replace(«Hello», «Hi»)
А полноценный интерпретатор – это целая библиотека из такого рода замен.
Главная шутка в том, что Python – это интерпретируемый язык программирования! К слову, Ассемблер тоже считывается машиной как двоичный код, правда через ассемблирование, но потому рекурсия самых возможных переводов, скажем, давно мейнстрим. Перевод в переводе, который перевод в переводе, который перевод в переводе…
Когда происходит запуск программы на Python, питоновский интерпретатор переводит написанный интуитивно понятный код в байт-код, а PVM исполняет байт-код команда за командой. Так и происходит вывод.
Работа с холстами
Итак, мы организовали выполнение функции по кнопке, написали простенький интерпретатор. Конечно, Tkinter позволяет много чего: создание таблиц, например. Это всё чрезвычайно круто и полезно, и перед изучением более сложных объектов (которые пойдут дальше) я очень рекомендую с этими более сложными структурами ознакомиться.
Но не будем оттягивать самое необходимое – Canvas (холсты), или то, на чём можно будет буквально рисовать необходимый UI. Прежде всего стоит включить свою фантазию на максимум при работе с данным типом объектов, потому что этот объект позволяет содержать иные объекты и управлять ими. Если всё прочее – файлы, то холсты – это папки, где их можно объединять, группировать. Но то, что в целом позволяет делать полноценное ПО, это возможность управления представленными файлами.
Это как HTML-сцена и JS одновременно: ты можешь расставлять объекты и задавать им поведение. Основные параметры Canvas (нашего холста) таковы: background (цвет фона), border (граница), borderwidth (толщина границы), height и width (высота и ширина соответственно).
Давайте создадим чудесный холст. Импортируем библиотеку (чтобы Python смог интерпретировать команды библиотеки «tkinter», небольшая отсылка):
from tkinter import *
Теперь создаём окно 520×520:
root = Tk()
root.title(«Example»)
root.geometry(«520×520»)
Создаём холст 250×250 и красим его в белый:
canvas = Canvas(bg=»white», width=250, height=250)
Организуем его по центру:
canvas.pack(anchor=CENTER, expand=1)
И запускаем таким образом, чтобы закрыть программу можно было только на крестик:
root.mainloop()
На данном холсте мы можем рисовать линии, овалы, прямоугольники, многоугольники, но нас сейчас больше интересуют картинки, текст и виджеты! Добавлять их можно следующим образом: «create_image()», «create_text()», «create_window()». Существует набор дополнительных параметров, в том числе параметров отрисовки: «fill» – цвет заполнения фигуры, «activefill» – цвет заполнения фигуры при наведении курсора, «activestipple» – даже шаблон заполнения фигуры при наведении курсора, и многие другие.
Создадим прокрутку, например:
canvas = tk.Canvas(parent)
Создали окно. Теперь ползунок справа:
scroll = tk.Scrollbar(parent, command=canvas.yview)
Теперь как бы связываем ползунок и холст:
canvas.configure(yscrollcommand=scroll.set)
Создаём то, что будет прокручиваться (потому что сам по себе холст не может этого, это лишь окно для отображения других объектов, а для прокрутки нужен… прокручиваемый объект):
canvas.create_window((0, 0), window=frame, anchor=»nw»)
И задаём холсту размер содержимого (насколько большой должна быть прокрутка):
canvas.configure(scrollregion=(0, 0, 500, 2000))
Также есть возможность сделать динамическую прокрутку в зависимости от содержащихся внутри объектов и так далее, в общем, как и во всех современных приложениях, веб-сервисах: «frame.bind(«<Configure>», lambda e: canvas.configure(scrollregion=canvas.bbox(«all»)))». Как это работает?
<Configure> – сообщает о том, что размер виджета изменился.
canvas.bbox(«all») – считает реальные границы того, что находится внутри холста.
scrollregion=… — обновляет область прокрутки при изменении.
Остальной инструментарий: для удаления объектов используется «delete()».
И теперь переходим к моему обещанию того, что это будет слишком напоминать браузерный подход:
Управление тегами!
Мы можем управлять тегами:
tags=[«НАЗВАНИЕ»], gettags(), addtag()
Удалять теги:
dtag()
И управлять событиями через теги:
tag_bind(тег, событие, функция)
Впрочем, почти как в связке HTML-JS: объект и событие могут быть также связаны через тег.
Декартова плоскость или геометрический бум
Упоминая работу с холстами, невозможно не объяснить ту механику, которая находится в ядре любых таких разработок. И HTML-JS, и Python-Tkinter, и всевозможные движки (Godot, Unity) работают по одному и тому же принципу: существует некоторая плоскость (будь то холст или сцена), имеющая N-ое количество координат (в двумерной плоскости – это X и Y, в трёхмерной добавляется ещё и Z-координата глубины).
При составлении UI для программ (будь то парсеры, компиляторы, интерпретаторы, иные виды ПО, кстати даже Word – это интерпретатор текста) будет необходимостью понимание механизмов расставления объектов по координатам на какой-либо плоскости. В том числе, если речь идёт про интерактив, важно и понимание механизмов их перемещения (например, как нужно менять координаты объекта, чтобы он перемещался по плоскости, др. задачи).
Даже при форматировании текста данный навык является ключевым, потому область требует внимания в любом случае. И почему люди перешли именно к такой схеме? Ранее были неудобные консольные форматы: приходилось всё вбивать текстом, чтобы ПК определял ввод и имел какие-то задачи. Декартова плоскость – это удобная модель для того, чтобы развернуть всё, до чего может только добраться человеческая фантазия, от узкоспециализированных редакторов до операционных систем. Возможно, теперь это можно называть геометрическим бумом. Честно – даже рад, что события развернулись именно так, ведь теперь программисты имеют мощный инструмент, а дизайнеры – простор для творчества.
Разумеется это roadmap. Данная работа задаёт лишь общее направление, а каждый отдельный её аспект требует более долгого и осмысленного погружения. Описана лишь поочерёдность – что, как, почему, зачем. Ну и базы добавил, как всегда.
