LINUX.ORG.RU

Tcl/tk. GUI на SVG-виджетах. Градиентная заливка и прозрачность. Часть III

 , , , ,


0

1

После выхода первой статьи про 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, с рассказа о которой и началось наше повествование.

СОДЕРЖАНИЕ

Tcl/tk. GUI на SVG-виджетах. Часть I

Tcl/tk. GUI на SVG-виджетах. Поддержка SVG-файлов. Часть II



Проверено: dataman ()
Последнее исправление: dataman (всего исправлений: 3)
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.