Tcl/tk: интегрированная среда разработки TKproE-2.30
Прошло без малого пять лет как я впервые столкнулся с интегрированной средой разработки программ на tcl/tk TKproE-2.20. И вот апреле 2021 года вышла новая версия этого продукта — TKproE-2.30 и я полностью погрузился в её мир. Он меня заворожил. Название TKproE является аббревиатурой от TCL/TK Programming Environment. TKproE — это интегрированная среда разработки программ для языка сценариев TCL/TK. Сам TKproE полностью написан на языке TCL/TK. В преамбуле к TKproE подчёркнуто, что он поддерживает быструю разработку сложных графических пользовательских интерфейсов.
Что же принципиально нового появилось в новой версии по сравнению с версией 2.20 помимо нового логотипа?
На мой взгляд, это две вещи. Первое, TKproE теперь распространяется не только в виде tcl-скрипта, но и в виде абсолютно самодостаточных бинарных исполняемых файлов. Второе, это добавление в проект кнопки «Build», которая позволяет получать бинарный код для любого проекта, создаваемого в рамках TKproE:
Нужно согласиться, что мало кто откажется от такого сервиса. Как говорится, пустячок, а приятно. Для создания бинарного кода используется враппер freewrap. Очень удобная вещь. Лично я долго пользовался этим враппером, пока мне на глаза не попался враппер tclexecomp, который сделан на базе всё того же проекта freewrap.
Прельстил он меня наличием в нём встроенного пакета (tcl-модуля) tls с поддержкой протокола tls1.3, без которого сегодня трудно обойтись. Например, доступ на GitHUB сегодня возможен только по протоколу tls1.3. Надо сказать, что и набор встроенных пакетов у враппера tclexecomp шире, чем у самого freewrap. Хотя понятно, что при желании нет проблем подключить новые пакеты, но для этого надо постараться. Помимо Linux и Windows tclexecomp портирован также и на платфорпму OS/X (macOS). Более того, начиная с версии 1.2.0, у этого враппера появился функционал, который позволяет оптимизировать состав пакетов, включённых во враппер, которым вы собираетесь пользоваться, или оптимизировать уже готовый бинарный файл. Для этого достаточно выполнить команду tclexecomp –gui:
Если необходимо оптимизировать уже собранный бинарный файл конкретного приложения, то достаточно временно переименуйте его так, чтобы он начинался с символов «tclexecomp» (как это видно на скриншоте). Но покритиковать tclexecomp причина всё же есть. Так, в его составе отсутствует кодировка cp1251.enc и даже ascii.enc. Отсутствуют также и русские словари ru.msg и для tcl и для tk. Конечно, это не страшно и они легко добавляются. Более того все эти недостатки и для freewrap и для tclexecomp устранимы и все новые дистрибутивы tkproe, которые будут представлены ниже, собраны без этих недостатков. Надо отметить, что наличие консоли freewrap или tclexecomp дает дополнительные возможности при разработке приложения:
Врапперы freewrap и tclexdecomp заслуживают отдельной публикации.
Но вот что интересно, что бинарный код для проекта TKproE средствами самого tkproe создать не удастся. TKproE, по понятным причинам, просто не загрузит свой исходный код.
Поэтому бинарный код для TKproE создается из командной строки. Создание бинарного кода для сторонних проектов в TKproЕ также не предусмотрено. А поскольку у меня возникли некоторые идеи по внесению дополнений в проект TKproE, то я счёл целесообразным начать дополнения функционала с вкладки Build, дополнив её возможностью собирать бинарный код для внешних проектов (в том числе и для tkproe). При этом учитывалось то, что бинарный код может собираться с использованием обоих врапперов и freewrap и tclexecomp.
После внесения дополнений, вкладка Build с использованием враппера freewrap при генерации бинарного кода для внешнего проекта выглядит так:
Как видим, появилась дополнительная кнопка «Build executable program for external project» (Создать бинарный код для внешнего проекта). Появилась область «Used wrapper», в которой указывается тип враппера, используемого для создания бинарного кода TKproE.
И самое главное, появилась возможность выбирать главный скрипт внешнего приложения, для которого создается бинарный код. При использовании враппера tclexecomp появляется возможность собирать бинарный код и для MacOS (OS/X):
Несомненным плюсом является то, что, если используется «завёрнутый» tkproe, он же может выступать в качестве обёртки для других приложений.
Что касается платформы macOS, то для большего единообразия на ней были заменены
…
#Проверяем платформу
global macos
set macos 0
switch [tk windowingsystem] {
classic - aqua {
#Платформа macOS
set macos 1
}
}
…
if {$macos } {
#Используется виджет label
label .tpmain.menubar.menubutton1 \
-activebackground {#dcdcdc} \
-background {blue4} \
-font {Helvetica 10} \
-foreground {white} \
-highlightbackground {#dcdcdc} \
-padx {5} \
-pady {0} \
-text {File} \
-underline {0}
bind .tpmain.menubar.menubutton1 {TP_ShowContextMenuOSX %W }
bind .tpmain.menubar.menubutton1 {%W configure -background #dcdcdc -foreground black
}
bind .tpmain.menubar.menubutton1 {global TPpressLab
if { "%W" != "$TPpressLab" } {
%W configure -background blue4 -foreground white
} else {
%W configure -background red1 -foreground white
}
}
} else {
#Используется виджет menubutton
menubutton .tpmain.menubar.menubutton1 \
-activebackground {#dcdcdc} \
-background {blue4} \
-font {Helvetica 10} \
-foreground {white} \
-highlightbackground {#dcdcdc} \
-menu {.tpmain.menubar.menubutton1.m} \
-padx {5} \
-pady {0} \
-text {File} \
-underline {0}
}
…
Что получилось можно видеть на следующем скриншоте, где вверху скриншот оригинального кода, а ниже скриншот с измененным кодом:
Поскольку было решено подготовить отдельную публикацию по врапперам freewrap и tclexecomp, то перейдём непосредственно к функционалу TKproE. В данной статье будет рассмотрена работа с холстом (canvas), графическими объектами на нём (линии, прямоугольники, изображения и т.д.) и сделанными дополнениями:
Первое, что было сделано, это была добавлена возможность рисовать прямоугольники с загругленными (round) или срезанными вершинами (bavel):
#Расчёт координат для прямоугольника с закругленными углами
# roundRect --
# Draw a rounded rectangle in the canvas.
# Parameters:
# x0, y0 - Coordinates of the upper left corner, in pixels
# x3, y3 - Coordinates of the lower right corner, in pixels
# radius - Radius of the bend at the corners, in any form
# acceptable to Tk_GetPixels
# Results:
# Returns the coordinates for polygon.
# Side effects:
# Creates a rounded rectangle as a smooth polygon in the canvas.
#----------------------------------------------------------------------
proc TP_roundRect { x0 y0 x3 y3 radius} {
set r $radius
set d [expr { 2 * $r }]
# Make sure that the radius of the curve is less than 3/8
# size of the box!
set maxr 0.75
if { $d > $maxr * ( $x3 - $x0 ) } {
set d [expr { $maxr * ( $x3 - $x0 ) }]
}
if { $d > $maxr * ( $y3 - $y0 ) } {
set d [expr { $maxr * ( $y3 - $y0 ) }]
}
set x1 [expr { $x0 + $d }]
set x2 [expr { $x3 - $d }]
set y1 [expr { $y0 + $d }]
set y2 [expr { $y3 - $d }]
set cmd [list ]
lappend cmd $x0 $y0
lappend cmd $x1 $y0
lappend cmd $x2 $y0
lappend cmd $x3 $y0
lappend cmd $x3 $y1
lappend cmd $x3 $y2
lappend cmd $x3 $y3
lappend cmd $x2 $y3
lappend cmd $x1 $y3
lappend cmd $x0 $y3
lappend cmd $x0 $y2
lappend cmd $x0 $y1
# lappend cmd -smooth 1
return $cmd
}
#Расчёт координат для прямоугольника со скошенными углами
proc TP_bevelRect { x0 y0 x3 y3 radius} {
set r [expr {$radius * -1 }]
set d [expr { 2 * $r }]
if { $d > ( $x3 - $x0 ) } {
set d [expr { ( $x3 - $x0 ) / 2}]
}
if { $d > ( $y3 - $y0 ) } {
set d [expr { ( $y3 - $y0 )/2 }]
}
set d [expr {$d / 2 }]
set x1 [expr { $x0 + $d }]
set x2 [expr { $x3 - $d }]
set y1 [expr { $y0 + $d }]
set y2 [expr { $y3 - $d }]
set cmd [list ]
lappend cmd $x0 $y1
lappend cmd $x1 $y0
lappend cmd $x2 $y0
lappend cmd $x3 $y1
lappend cmd $x3 $y2
lappend cmd $x2 $y3
lappend cmd $x1 $y3
lappend cmd $x0 $y2
return $cmd
}
Конечно, их можно нарисовать и как полигон, но сколько надо будет написать цифр или выполнить подгонку фигуры вручную!
И хотелось иметь под рукой кнопку получения скриншотов! И я добавил эти функции:
Кнопка «Screenshot» делает скриншот выбранного виджета. В данном случае выбран виджет canvas и будет сделан скриншот всего canvas, сохраняя заливку canvas (атрибут –background). Если же мы выделим все или отдельные объекты canvas с помощью функции GrpMove или новой функции GrpResizeMove, а затем сохраним выделенную область с помощью одной из функций SaveGroupToFile или SaveGroupToImage, то в этом случае заливка canvas будет отсутствовать и в скриншот попадет только выделенная область с находящимися в ней объектами (процедура TP_saveGroupToFileOrImage в файле tkproe_extention.tcl).
Для получения скриншота виджета создаётся его изображение с использованием следующей функции:
$image create photo –format window –data <идентификатор окна>.
Функция «SaveGroupToFile», аналогично функции «Screenshot», сохраняет полученное изображение выделенной области в файле. Функция «SaveGroupToImage» позволяет сохранить изображение, как будет показано ниже, в проекте.
Когда началось тестирование новых функций ScreenShot, SaveGroupToFile, SaveGroupToImage, SaveOneToFile, SaveOneToImage на платформе macOS, то выяснилось, что на ней эти функции не работают:
«Window "имярек" cannot be transformed into a pixmap (possibly obscured?)»
При этом само окно canvas-а на экране отображается. Использование функций raise и update (есть такие советы в Интернете) тоже не дали положительного результата. Где-то на просторах интернет я видел, что это может зависеть от версии операционной системы (как будет показано ниже, это ошибочный посыл), но в итоге для сохранения объектов canvas в виде изображений в различных форматах мне пришлось задействовать функцию postscript, а затем из полученного postscript-а создавать изображение:
image create photo –format ps –data [ postscript <х> ]
Кстати, этот код прекрасно работает на всех платформах. Но на платформе MacOS при использовании функции postscript должен быть установлен пакет ghostscript:
$brew install ghostscript
Однако использование функции postscript работает только с виджетом canvas и не даёт возможности сделать скриншоты средствами tcl/tk других виджетов на macOS.
Что я только не делал и не перепробовал.
Запускал на macOS wish и подгружал пакет Img 1.4.6, запускал tclexeccomp-1.2.0 и подгружал его пакет Img. Результат тот же. Я посмотрел, какая версия tcl/tk стоит на моём компьютере. Оказалась 8.6.9. Посмотрел в tclexecomp-1.2.0. Здесь версия оказалась 8.6.10. Результат тот же. Тогда я решил попробовать старую проверенную версию tclexecomp-1.0.4, её я использую уже несколько лет и она меня ни разу не подвела.
Не подвела она и сейчас:
Команда «image create photo –format window» успешно сработала!!!
Я успокоился и решил методично исследовать эту проблему.
Что я имел? Имел то, что при использовании версии tcl/tk 8.6.6 на macOS всё работает. А вот версии 8.6.9 и 8.6.10 отказываются выполнять эту команду.
Текущей версией tcl/tk на этот момент была версия 8.6.11. Я решил её собрать. При чём собрать как у себя на компьютере, так и на стороне. Для локальной сборки я использовал проект BAWT. Проект хорош ещё тем, что он включает в себя множество, помимо tcl/tk, других пакетов, в том числе и пакет Img.
Никаких проблем и заморочек при сборке проекта я не встретил.
Для сторонней сборки был выбран проект KitCreatot, в рамках которого можно легко и просто собрать tclkit для различных платформ удалённо в режиме online:
Надежды не оправдались, на версии tcl/tk 8.6.11 функция «image create photo –format window» на платформе macOS не работала. Наступило отчаяние, на версии 8.6.6 всё работает, а на более поздних версиях работать перестало. Лезть в исходный код самого интерпретатора tcl/tk как-то не хотелось. На всех других платформах, включая Android, всё работало без проблем. Вывело из состояния стагнации попавшаяся на глаза в ноябре 2021 года новость о выходе нового резиза Tcl/Tk 8.6/12 с многообещающей фразой:
В Tk продолжена работа по улучшению поддержки платформы macOS.
Я тут же решил проверить функциональность этого релиза. Скачал BAWT, который уже базировался на новой версии, и собрал его. Затем зашёл на сайт KitCreator-а, и собрал tclkit на базе версии 8.6.12. И только после этого проверил их работоспособность:
Обе сборки оказались поддерживающими функцию
image create –format window –data
Всё встало на свои места, и можно было двигаться дальше.
Возвращаемся к добавленным функциям для работы с объектами canvas. Вот эти функции (см. скриншот выше):
- GrpResizeMove;
- Reshape Polygon/Line;
- Save GroupToFile;
- SaveGroupToImage;
- SaveOneToImage;
- SaveOneToFile
Функция GrpRezizeMove, аналогично функции GrpMove, позволяет выделить в canvas область с объектами, переместить выделенную область на новое место и при необходимости изменить размеры:
Функция «Reshape polygon/line» позволяет манипулировать poligon-ами и линиями. Достаточно курсором мыши щелкнуть по одному их этих объектов, как появится реперные точки, потянув за которые можно изменить внешний вид:
Реперные точки можно и добавлять и удалять. Для уничтожения реперной точки необходимо курсор мыши навести на точку, предназначенную для уничтожения, нажать клавишу Alt и щелкнуть левой кнопкой мыши. Для добавления точки также необходимо подвести курсор мыши к ближайшей реперной точки, около которой планируется добавить новую точку, нажать клавишу Ctrl и щёлкнуть по левой кнопке мыши.
Функции «SaveGroupToFile» и «SaveGroupToImage» позволяют сохранить выделенную область canvas как изображение в проекте (image create photo) или в файле. Для выделения области можно использовать уже знакомые функции «GrpMove» или «GrpResizeMove». Это удобно не только при подготовке иллюстраций, скажем, для документации, но и для формирования интерфейса проекта. Например, при разработки утилиты cryptoarmpkcs для работы с электронной подписью на платформе Android таким способом формировались информационные «облака»:
Сначала на canvas-е рисовался прямоугольник с круглыми вершинами, рядом рисовался «язычок» и писАлся текст. Затем, используя функцию «OneMove» (не возбраняется и воспользоваться функцией GrpResizeMove или даже ObjMove), эти объекты сводятся в нужное изображение. Для выделения полученного изображения используем функцию GrpResizeMove. После выделения области нажимаем кнопку (вызываем функцию) SaveGroupToImage:
В появившемся окне «TKproE image management» можно установить те или иные параметры изображения и сохранить его. По умолчанию изображения сохраняются в png-формате (см. красный квадрат на скриншоте). Это позволяет использовать alpha-канал и создавать прозрачные изображения (параметр -alpha). После создания и сохранения нужного нам изображения, все его составные части можно удалить из проекта и дальше работать только с самим изображением:
В нашем примере мы привязали к нажатию кнопки «Конец работы» вызов функции butImg, которая отображает наше информационное облачко и, если по нему не было нажатия, то через 5 секунд убирает его с экрана.
Особо следует остановиться на функциях SaveOneToImage и SaveOneToFile. Эти функции позволяют сохранить в виде изображения один выбранный объект на холсте (на canvas-е) в файле или как объект image. Казалось бы что особенного, тем более что есть функции SaveGroupToFile и SaveGroupToImage. Но можно создать произвольный прозрачный (атрибут fil должен быть пустым) объект (прямоугольник, полигон, овал и т.д.) вокруг требуемого участка холста, при необходимости сделать окантовку этого объекта нулевой толщины и прозрачной (атрибут outline должен быть пустым) и, применив одну из вышеуказанных функций, получим изображение этого участка:
Если вы спросите, а зачем при нулевой толщине окантовки делать её прозрачной, то ответ будет таков: polygon не дает возможности сделать его окантовку нулевой толщины. Наверное, это баг.
Хотел добавить ещё функции, связанные с трансформацией изображений, но решил остановиться (пока).
С моей точки зрения, TKproE является отличным учебным пособием.
Основные дополнения собраны в файле tkproe_extention.tcl.
Исходный код и дистрибутивы для платформ Linux, Windows и macOS можно получить здесь.
Доработанную утилиту tkproeplus для разных платформ можно скачать здесь:
Знакомство с TKproE будет полезно и для тех, кто программирует на Python с использованием Tkinter.
P.S.
Все иллюстрации были подготовлены средствами TKproEplus.