После выхода первой статьи про svg-виджеты для tcl/tk прошло почти полгода. За это время пэт-проект возмужал и продолжает взрослеть. Напомним, что проект svgwidgets, примеры и интерпретаторы tcl/tk с необходимыми пакетами для разработки gui на базе svg-виджетов можно найти на github-е. В проекте svgwidgets на github-е можно найти как версию интерпретатора tclexecomp как для Linux64 (папка tclexexcomp902), собранного из исходников tcl/tk-9.0.2, так и версию интерпретатора на базе tcl/tk-8.6 для платформы Linux64 (папка tclexecomp200). К ранее созданному на github-е подкаталогу examples/CryptoArmPKCS_Test, в котором выложен исходный код криптографической утилиты для работы с электронной подписью cryptoarmpkcs, который предназначен для запуска на платформе Linux64 в среде tcl/tk-9, добавлена аналогичная папка для запуска утилиты cryptoarmpkcs в среде tcl/tk-8.6 на платформе Linux64 (папка examples/CryptoArmPKCS_Test_Tk86). Для запуска этой утилиты ничего дополнительного устанавливать на свой компьютер не требуется. Достаточно выбрать соответствующий интерпретатор из папки tclexecomp200 или tclexexcomp902 и выполнить файл mainguipkcs_svg.tcl из соответствующей папки ~/examples/CryptoArmPKCS_Test, например:
bash-5.2$ tclexecomp902/tclexecomp64_902_Lin64 examples/CryptoArmPKCS_Test/mainguipkcs_svg.tcl
При подготовке этой статьи использовался графический интерпретатор tclexexcomp902, который поддерживает tcl/tk-9. Напомним, что svg-виджеты могут создаваться каждый на отдельном холсте, а затем отображаться с помощью одного из диспетчеров компоновки (pack, grid, place), или могут создаваться все или группа виджетов на одном и том же холсте. При размещении нескольких svg-виджетов на одном холсте диспетчеры компоновки не используются, их размещение на холсте задается опциями –x и –y.
P. S. Пожалуйста, не судите мои цветовые предпочтения и дизайнерские способности слишком строго. Здесь демонстрируются возможности инструмента
Начнем с варианта, когда каждый svg-виджет создаётся на отдельном холсте, предварительно запустив интерпретатор tclexexcomp902:
bash-5.2$ tclexecomp902/tclexecomp64_902_Lin64
В этом случае для размещения svg-виджетов будут использоваться диспетчеры компоновки (pack, grid, place), а опции -x и -y будут игнорироваться. Итак, для начала загружаем пакет svgwidgets и создаём окно .win размером 7 см. на 12 см.:
package require svgwidgets
toplevel .win -bg yellow
wm geometry .win [winfo pixels .win 7c]x[winfo pixels .win 12c]
Следует иметь в виду, что точность соответствия размеров окна задаваемым значениям ширины и высоты зависит от значения коэффициента масштабирования (tk scaling), который на моем компьютере в момент написания статьи был равен 1.8. Теперь можно создать svg-фрейм с идентификатором frame1 на холсте .win.fr1 и разместить его в созданном окне .win:
cbutton create frame1 .win.fr1 -type frame -strokewidth 2m -stroke cyan -rx 0 -fillnormal white
#Можно svg-фрейм создать и так:
#cframe create frame1 .win.fr1 -type frame -strokewidth 2m -stroke cyan -rx 0 -fillnormal white
#Размещение svg-фрейма в окне
pack [frame1 canvas] -in {.win} -fill both -expand 1
#Или
#pack .win.fr1 -fill both -expand 1
Можно также вместо обычного svg-фрейма создать и svg-фрейм с заголовком, предварительно уничтожив созданный фрейм frame1:
frame1 destroy
cframe create frame1 .win.fr1 -type clframe -text {Фрейм с заголовком} -strokewidth 2m -stroke cyan -rx 0 -fillnormal white -fillbox cyan -fontsize 5m
#Прямоугольное выделение заголовка фрейма (цвет: -fillbox)
frame1 boxtext
Если задать опцию -rx равную 0 (нулю), то фрейм будет прямоугольной формы. Если окантовка не требуется, то достаточно задать опции -stroke {} -strokewidth 0. Заливка фрейма определяется опцией -fillnormal.
Теперь создадим на холсте .win.fr1, на котором уже располагается svg-фрейм frame1, несколько svg-виджетов класса cbutton (кнопки), размещаемые вертикально (используется компоновщик pack):
#Создаем пять кнопок: "rect round ellipse square circle "
#При нажатии на кнопку печатается её идентификатор
foreach tbut "rect round ellipse square circle " {
cbutton create b$tbut .win.$tbut -type $tbut -text "ID: b$tbut" -command "puts \"Нажата кнопка b$tbut]\""
pack .win.$tbut -in .win.fr1 -fill both -expand 1 -padx 1c -pady {5m 0}
if {$tbut == {circle}} {
pack configure .win.circle -pady {5m 1c}
} elseif {$tbut == {rect}} {
pack configure .win.rect -pady {1c 0}
}
}
И здесь может произойти конфуз. Если при создании кнопки цвет, задаваемый опцией -bacground, будет отличаться от значения опции -fillnormal фрейма, в котором размещается кнопка, то этот цвет будет как бельмо в глазу. А если у фрейма градиентная заливка, то тем более. Для решения этой проблемы у svg-виджетов имеется метод fon, который нужно применить к каждой кнопке. Задачей этого метода является сделать снимок участка экрана, на котором будет размещен svg-виджет и разместить этот снимок на нижнем слое холста виджета. Другого способа согласования заливки при компоновки найти не удалось. Для реализации этого метода потребовался пакет treectrl, в котором есть функция loupe. Эта функция позволяет делать снимок любого участка экрана, а при желании, и масштабировать его. Отсюда и название функции loupe:
loupe <имя image> <x> <y> <w> <h> [<zoom>],
где поле <имя image> содержит идентификатор изображения, создаваемого командой image:
image create photo –width <w> -height <h>
Координаты x и y задают центр снимаемой области (подчеркиваю, ЦЕНТР), w – это ширина области, а h – высота снимаемой области. Таким образом, функция loupe снимает (делает скриншот) участок со следующими координатами:
X0 = x – w/2, y0 = y – h/2
X1 = x + w/2, y1 = y + h/2
Применение метода fon к виджетам, у которых есть рассогласование в цветах, нормализует обстановку. Метод fon целесообразно использовать только, если используется градиентная заливка или используется прозрачность -fillopacity < 1.0 и при использовании метода компоновки place. Но есть более изящный способ, который сам решает вопрос согласования цветов. Он состоит в том, чтобы, после размещения на фрейме всех виджетов, просто переустановить заливку -fillnormal или прозрачность -fillopacity этого фрейма:
<id-фрейма> config -fillnormal [<id-фрейма> config -fillnormal]
#или
<id-фрейма> config -fillopacity [<id-фрейма> config -fillopacity]
В нашем примере (левый скриншот в левом верхнем углу логотипа статьи) это выглядит так:
frame1 config -fillnormal [frame1 config -fillnormal]
Сразу отметим, что фрейм масштабируется при изменении размеров окна, в котором он размещен (команда wm geometry <окно> <шипина>x<высота>[+x+y] или растягивании (сжатии) окна за одну из его вершин или одну из его сторон) (средний скриншот в левом верхнем углу логотипа статьи):
wm geometry .win [winfo pixels .win 8c]x[winfo pixels .win 13c]
Здесь мы применили команду wm geometry для увеличения окна .win до размеров 8 см. на 13 см. Напомним ещё раз, что основные претензии, которые порой предъявляются классическим tk-виджетам, связаны с тем, что они имеют прямоугольную форму, что используется только одноцветная заливка виджетов и отсутствует возможность задавать прозрачность у виджетов. Все эти недостатки отсутствуют у svg-виджетов. Для примера, установим градиентную заливку на svg-фрейме frame1 (см. средний скриншот скриншот в левой половине логотипа статьи):
#Вызов генератора градиента
set grad1 [ ::gengrad::generateGradient .win.fr1]
#Утилита генерации градиента завершена.
#gradient create linear -method pad -units bbox -stops { { 0.00 #57f1b3 1.00} { 1.00 #c63e31 1.00}} -lineartransition {0.00 0.00 0.00 1.00}
#Создание градиентой заливки
set ngrad1 [eval [frame1 canvas] $grad1]
#Установка градиента на svg-фрейм frame1:
frame1 config -fillnormal $ngrad1
Для полноты картины покажем как работает прозрачность. Прозрачность регулируется двумя опциями. Опция -fillopacity устанавливает степень прозрачности собственно заливки, а опция -strokeopacity – прозрачность окантовки. В нашем примере (см. правый скринщот выше) установим для svg-фрейма frame1 прозрачность равную 0.7, полную прозрачность для кнопки bround и частичную прозрачность для кнопки bellipse:
bround config -fillopacity 0
bellipse config -fillopacity 0.5
frame1 config -fillopacity 0.7
Обратите внимание, что прозрачность у фрейма frame1 устанавливается в последнюю очередь. И вы, наверное, заметили моргание на своём экране. Это и есть на сегодня главный недостаток размещение каждого виджета на персональном холсте. Мерцание проявляется при изменении размеров окна. И пока не удаётся разрешить эту проблему.
В папке examles на github-е можно посмотреть примеры скрипт_button_PACK_gradient.tcl и скрипт_button_PACK_gradient_opacity.tcl (см. средний скриншот скриншот в левой половине логотипа статьи). В этих примерах при щелчке правой кнопкой мыши на главном окне примера будет напечатан идентификатор svg-виджета, на котором находится курсор. В этих примерах это обеспечивает вызов процедуры selwsvg. Это может помочь в экспериментах с заливкой, прозрачностью и т. п. в этих примерах.
Если используется размещение svg-виджетов на одном холсте, то мерцание экрана не происходит. Посмотрим, как изменится наш пример, если все виджеты располагаются на одном холсте. Для начала уничтожим все созданные кнопки, фрейм frame1 и окно .win:
foreach tbut "rect round ellipse square circle" {
b$tbut destroy
}
frame1 destroy
destroy .win
В этом случае для размещения svg-виджетов нам не потребуется использовать диспетчеры компоновки (pack, grid, place), расположение каждого виджета на холсте будет задаваться опциями -x и -y. Что касается создание окна и главного svg-фрейма frame1, то все аналогично предыдущему примеру:
package require svgwidgets
toplevel .win -bg yellow
wm geometry .win [winfo pixels .win 7c]x[winfo pixels .win 12c]
cbutton create frame1 .win.fr1 -type frame -strokewidth 2m -stroke cyan -rx 0 -fillnormal white
pack [frame1 canvas] -in {.win} -fill both -expand 1
А вот что касается самих кнопок и их размещения, то код будет несколько иной:
#Расстояние между виджетами по вертикали 15 миллиметров
set y0 [winfo pixel .win.fr1 15m]
#Координата по вертикали для первой кнрпки
set y1 $y0
#Создаем пять кнопок: "rect round ellipse square circle "
#При нажатии на кнопку печатается её идентификатор
foreach tbut "rect round ellipse square circle " {
cbutton create b$tbut .win.fr1 -type $tbut -x 2c -y $y1 -text "ID: b$tbut" -command "puts \"Нажата кнопка b$tbut]\""
incr y1 $y0
}
Результат можно видеть ниже на левом скриншоте в правом верхнем углу логотипа статьи. Дальше опять без изменений как в предыдущем примере меняем размеры и делаем градиентную заливку (средний скриншот в правом верхнем углу логотипа):
wm geometry .win [winfo pixels .win 8c]x[winfo pixels .win 13c]
set ngrad2 [[frame1 canvas] gradient create linear -method pad -units bbox -stops { { 0.00 #57f1b3 1.00} { 1.00 #c63e31 1.00}} -lineartransition {0.00 0.00 0.00 1.00}]
frame1 config -fillnormal $ngrad2
Далее устанавливаем прозрачность (правый скриншот):
frame1 config -fillopacity 0.7
bround config -fillopacity 0
bellipse config -fillopacity 0.5
Здесь последовательность смены прозрачности не имеет значения и никакого мерцания не наблюдается. Изменить начальное значение координат виджета, задаваемых опциями -x и -y в методе config нельзя, но можно передвигать svg-виджет по холсту:
<идентификатор виджета> move <dx> <dy>,
где dx и dy смещение по осям x и y. Например, требуется переместить виджет bcircle вправо на 5 миллиметров и вниз на один сантиметр:
bcircle move 5m 1c
Для возврата в исходное положение потребуется выполнить следующую команду:
bcircle move -5m -1c
Если оба смещения будут нулевые, то команда вернет общий tag для всех графических составляющих svg-виджета, например:
bcircle move 0 0
В нашем случае это будет canvasbbcircle. Правда, эта команда может заинтересовать только очень продвинутых знатоков svg-графики. Метод state управляет состоянием svg-виджета:
<идентификатор виджета> state [hidden | disabled | normal], где
hidden – сделать виджет невидимым;
disabled – заблокировать виджет;
normal – вернуть виджет в нормальное состояние.
Для примера сначала скроем виджет bcircle, а через 2 секунды вернем его в нормальное состояние:
bcircle state hidden
update
after 2000
bcircle state normal
Аналогичного эффекта можно достичь, применяя метод config для опции -state:
<идентификатор виджета> config -state [hidden | disabled | normal]
Более полный и интересный пример можно найти на github-е в папке examples, файл скрипт_button_Холст.tcl (скринщот в правом нижнем углу логотипа статьи). В этом примере также при щелчке правой кнопкой мыши на главном окне примера будет напечатан идентификатор svg-виджета, на котором находится курсор. В примере это реализуется через вызов процедуры selectwsvg. Это может помочь в экспериментах с заливкой, прозрачностью и т. п. в данном примере.
Но если, всё же, используется вариант создания каждого svg-виджета на отдельном холсте при разработки GUI, но все вопросы с заливкой и прозрачностью решаются на этапе проектирования, то этот недостаток проявляется очень редко. Именно это и демонстрирует GUI утилиты CryptoArmPKCS, с рассказа о которой и началось наше повествование.
СОДЕРЖАНИЕ