С момента написания на tcl/tk удостоверяющего центра CAFL63 и утилиты cryptoarmpkcs для работы с электронной подписью меня не покидала мысль, что неплохо бы оформить их как облачные сервисы. Я постоянно смотрел в сторону проекта CloudTk. Более того, я имел уже опыт создания облачного сервиса на TclTk, но это был опыт разработки web-приложения на базе TclHttpd (можно войти на сервис и по https):

Но это немножко не то, чего хотелось. А хотелось, чтобы разработанные утилиты запускались как автономно на компьютере, так и на сервере, а доступ к ним осуществлялся через браузеры. И еще было желание разместить в облаке примеры SVG-виджетов.
Но всё что-то держало. Это была и работа над моим пэт-проектом svgwidgets, и публикация книги «История моей страны. Записки военного инженера программиста», и участие в жизни «Музея СССР», прежде всего развитие его экспозиции ретро компьютеров, да и просто текущие дела в наше непростое время.
Особенно насыщенным выдался август текущего года. Помимо поездки по российской глубинке, мне посчастливилось встретиться с нашими выдающимися актерами и режиссёрами Михалковым Никитой Сергеевичем и Бурляевым Николаем Петровичем:

Не могу не сказать, что на фотографии чуть позади нас с Н.П. Бурляевым стоит известный художник , член корреспондент Российской академии художеств Юрий Леонидович Куперман. Его послужной список огромен. Станковые произведения художника хранятся в собраниях Государственной Третьяковской галереи, Музея Московского академического театра, Государственного центрального театрального музея имени А.А. Бахрушина, Государственного музея изобразительных искусств имени А.С. Пушкина, Метрополитен-музея и Библиотеки конгресса США, Министерства культуры Франции и других.
Обменялись мы книгами и c Михалковым Н.С:

Более того, он подарил мне и свою фирменную бейсболку. Надо сказать, что бейсболка Никиты Михалкова не уступает фирменной бейсболке ДональдаТрампа:

Я не утерпел и показал Никите Сергеевичу введение ко второму изданию своей книги и сказал пару слов про искусственный интеллект и про то, как у меня просили автографы, путая с ним. Ответ был таков: "Давай и дальше!". В итоге, на введении появился его автограф:

Вот расшифровка автографа: «На память, Н.С. Михалков, 21.08.2025, Чебоксары».
После такого августа я с новыми впечатлениями вернулся к своей «хотелке». Оказать помощь в реализации этой «хотелки» должен был проект CloudTk.
И начну я изложение с конечного результата:

Доступ по https к этому сервису можно получить здесь (не обессудьте, SSL-сертификаты - самоподписанные).
На стартовой странице фиксируется время обращения к облачному сервису, показывается текущее время, а также спешащий куда-то старый добрый паровоз. Если вы хотите увеличить скорость паровоза, то щелкните левой кнопкой мыши, а захотите остановиться – щелкните правой кнопкой. Для перехода на страницу со списком демонстраций (скриншот «Tk Application») воспользуйтесь кнопкой «Перейти на демо-страницу». Для запуска приложения необходимо его выбрать, а затем нажать кнопку «Submit Query». В данной иллюстрации была выбрана демонстрация использования svg-виджетов при разработке игры в шашки (wsvgGame). «Игра» громко сказано, но очередность ходов игроками соблюдается и шашки можно ставить только на свободные клетки. Можете попробовать и сами все увидите. Главное здесь было показать возможности svg-виджетов. Этой же цели служат и демонстрации wsvgButton, wsvgCanvas и wsvgGradient.Здесь же можно и расслабиться и поиграть в старый добрый тетрис (Tetris), а то и в «биллиард» (TkPool):

Если вы выберите кнопку «CAFL63_P11» то у вас запустится удостоверяющий центр:

Выбрав кнопку «CryptoArmPKCS7» запустите утилиту для работы с электронной подписью:

Для возврата на страницу приложений используйте кнопку «На предыдущую страницу» (<-). Для перехода со страницы «Tk Application» на главную страницу просто щелкните по иконке «Tcl POWERED».
Но это конечный результат, а ниже будет рассказ как это было достигнуто.
В качестве платформы используется Linux x86_64.
Итак, идем на страницу CloudTk. И тут выясняется, что нам потребуется TclKit и starkit CloudTk.kit. Создаем папку для проекта, например, «~/testCloud» и скачиваем в неё starkit CloudTk.kit.
Можно также скачать и tclkit, но он будет очень древним. Правда есть online-страница для его генерации, но и здесь можно собрать tclkit только для tcl-8.6.12.
Поэтому было решено собрать tclkit самостоятельно. Это оказалось достаточно просто.
Сначала скачиваем проект:
$git clone https://github.com/rkeene/KitCreator.git
Проект размещается в папке KitCreator.
Ниже приводится patch для KitCreator-а.
Патч для KitCreaor версии 8.6.12
diff -ruN KitCreator/build/test/test KitCreator-8.6.17/build/test/test --- KitCreator/build/test/test 2025-09-25 10:55:57.345565571 +0300 +++ KitCreator-8.6.17/build/test/test 2025-09-25 10:59:42.197572433 +0300 @@ -1,6 +1,6 @@ #! /bin/bash -VERSIONS="8.5.19 8.6.12 fossil_trunk" +VERSIONS="8.5.19 8.6.12 9.6.17 fossil_trunk" # Find the base directory for x in 1 2 3 4 __fail__; do diff -ruN KitCreator/build/web/kitcreator.vfs/index.rvt KitCreator-8.6.17/build/web/kitcreator.vfs/index.rvt --- KitCreator/build/web/kitcreator.vfs/index.rvt 2025-09-25 10:55:57.347565571 +0300 +++ KitCreator-8.6.17/build/web/kitcreator.vfs/index.rvt 2025-09-25 11:00:54.503574640 +0300 @@ -73,6 +73,7 @@ set tcl_versions(8.6.10) 8.6.10 set tcl_versions(8.6.11) 8.6.11 set tcl_versions(8.6.12) 8.6.12 + set tcl_versions(8.6.17) 8.6.17 set tcl_version_list [lsort -dictionary [array names tcl_versions]] set tcl_version_selected [lindex $tcl_version_list end] diff -ruN KitCreator/kitcreator KitCreator-8.6.17/kitcreator --- KitCreator/kitcreator 2025-09-25 10:55:57.347565571 +0300 +++ KitCreator-8.6.17/kitcreator 2025-09-25 11:02:08.425576896 +0300 @@ -13,7 +13,7 @@ esac # Determine which Tcl version to build -TCLVERS="8.6.12" +TCLVERS="8.6.17" if echo "$1" | grep '^[0-9][0-9]*\.' >/dev/null || echo "$1" | egrep '^(cvs|fossil)_' >/dev/null; then TCLVERS="$1" @@ -69,7 +69,7 @@ # Add packages implied by the additional arguments if [ -z "${KITCREATOR_PKGS}" ]; then - KITCREATOR_PKGS="tk itcl mk4tcl" + KITCREATOR_PKGS="tcllib tk itcl mk4tcl" fi CONFIGUREEXTRA="$@" diff -ruN KitCreator/tcl/build.sh KitCreator-8.6.17/tcl/build.sh --- KitCreator/tcl/build.sh 2025-09-25 10:55:57.349565571 +0300 +++ KitCreator-8.6.17/tcl/build.sh 2025-09-25 11:03:27.005579294 +0300 @@ -58,6 +58,9 @@ 8.6.12) SRCHASH='26c995dd0f167e48b11961d891ee555f680c175f7173ff8cb829f4ebcde4c1a6' ;; + 8.6.17) + SRCHASH='a3903371efcce8a405c5c245d029e9f6850258a60fa3761c4d58995610949b31' + ;; esac # Set configure options for this sub-project diff -ruN KitCreator/tcllib/build.sh KitCreator-8.6.17/tcllib/build.sh --- KitCreator/tcllib/build.sh 2025-09-25 10:55:57.349565571 +0300 +++ KitCreator-8.6.17/tcllib/build.sh 2025-09-25 11:04:35.307581378 +0300 @@ -2,6 +2,6 @@ # BuildCompatible: KitCreator -version='1.20' +version='2.0' url="http://sourceforge.net/projects/tcllib/files/tcllib/${version}/tcllib-${version}.tar.bz2" -sha256='3a33f212f35235f9dca4425c5c5692186515be169b0b6e3ca498e7c344ea83b8' +sha256='196c574da9218cf8dcf180f38a603e670775ddb29f191960d6f6f13f52e56b04' diff -ruN KitCreator/tk/build.sh KitCreator-8.6.17/tk/build.sh --- KitCreator/tk/build.sh 2025-09-25 10:55:57.351565571 +0300 +++ KitCreator-8.6.17/tk/build.sh 2025-09-25 11:05:26.135582929 +0300 @@ -58,6 +58,9 @@ 8.6.12) SRCHASH='12395c1f3fcb6bed2938689f797ea3cdf41ed5cb6c4766eec8ac949560310630' ;; + 8.6.17) + SRCHASH='e4982df6f969c08bf9dd858a6891059b4a3f50dc6c87c10abadbbe2fc4838946' + ;; esac # Set configure options for this sub-project
Сохраним патч в файле PATCH_12_17.patch и пременим его к скачанной версии KitCreator-а:
$patch -s -p0 < PATCH_12_17.patch
Замечу, что этот патч добавляет в tclkit и пакет tcllib-2.0.
После внесения изменений заходим в каталог KitCreator и выполняем две команды:
$sh build/pre.sh $./kitcreator
После сборки в нашей папке найдем собранный модуль с именем tclkit-8.6.17.
Запускаем его и проверяем, что мы получили именно то, что "заказывали":
$./tclkit-8.6.17 %info tclversion 8.6 %info patchlevel 8.6.17 %package require tcllib 2.0 %exit $
Полученный модуль копируем в /usr/local/bin.
Убеждаемся, что на нашем компьютере установлен сервер Xvnc (TigerVNC) и оконный менеджер Matchbox.
Теперь идем в ранее созданную папку «~/testCloud», в которой уже лежит скачанный старкит CloudTk.kit, и создаём папку certs для ssl-сертификатов (если не предполагается использование https, то этот пункт можно опустить). Самое простое создать закрытый ключ и самоподписанный сертификат с помощью команды openssl:
$openssl req -new -x509 -days 3650 -nodes -out certs/server.pem -keyout certs/skey.pem … Country Name (2 letter code) [XX]:ru State or Province Name (full name) []:Moskouw Locality Name (eg, city) [Default City]: Organization Name (eg, company) [Default Company Ltd]: Organizational Unit Name (eg, section) []:user Common Name (eg, your name or your server's hostname) []:testCloudtk Email Address []:vorlov@lissi.ru $
Теперь выполняем команду:
$/usr/local/bin/tclkit-8.6.17 CloudTk.kit:
can't find package limit
Running with default file descriptor limit
/debug user "debug" password "..20+lmn+n5p"
httpd started on port 8015
secure httpd started on SSL port 8016
В том, что не найден пакет limit, ничего страшного нет.
Порты 8015 и 8016 присваиваются по умолчании. Для явного указания портов используются параметры «-port <номер порта>» и «-https_порт <номер порта для https-протокола>».
В браузере набираем http://localhost:8015/ или https://localhost:8016/ и попадаем на домашнюю страницу CloudTk.
Её следует изучить. Но где же приложения Tk?
Чтобы попасть на страницу с игрой в «TkPool» («биллиард») следует набрать в браузере либо http://localhost:8015/cloudtk/
либо
https://localhost:8016/cloudtk/
Следует обратить внимание на завершающую наклонную черту («/»).
После нажатия на кнопку «Enter» в браузере появится страницв «Tk Application» с radio-кновкой с именем TkPool. И если нажать на кнопку «Submit Query», то в браузере появится и «биллиард».
После запуска CloudTk.kit в нашем папке ~/testCloud появились новые каталоги:
drwxr-xr-x 2 4096 сен 22 19:01 auth
drwxr-xr-x 2 4096 сен 22 19:01 conf
drwxr-xr-x 2 4096 сен 22 19:01 htdocs
drwxr-xr-x 2 4096 сен 22 19:01 log
drwxr-xr-x 11 4096 сен 22 19:01 noVNC
drwxr-xr-x 3 4096 сен 22 19:01 Tk
Заглянем в папку «Tk». В ней содержится папка TkPool., в которой присутствует файл TkStartup.tcl. Именно в нем прописывается имя файла, с которого начинается загрузка пользовательского проекта.
По образцу и подобию папки TkPool пользователь может добавить свои проекты.
В качестве примера приведем содержимое каталога для запуска утилиты cryptoarmpkcs из демонстрационного проекта CryptoArmPKCS7:
$ls -Rl CryptoArmPKCS7/ CryptoArmPKCS7/: alloids.tcl card_and_pero_pkcs11_pkcs12.png classtoken_svg.tcl GostPfx.tcl guipkcs_oo_svg_TEST_STtoplevel.tcl Lcc.p12 Lrnd.p12 mainguipkcs_svg.tcl mecrown.png mtoken_withoutbg.png PKCSimage tclpkcs11.p11 TkStartup.tcl tokens_tcl_pero_85x45.png CryptoArmPKCS7/PKCSimage: certificate_blue.svg digital_signature_2.svg signeddoc.svg update.svg viewcert.svg $
В папке Tk/CryptoArmPKCS находятся все файлы, которые необходимы для запуска утилиты cryptoarmpkcs. Смотрим содержимое файла TkStartup.tcl:
$ cat CryptoArmPKCS7/TkStartup.tcl set ix [lsearch $argv -display] if {$ix >= 0} { incr ix set env(DISPLAY) [lindex $argv $ix] set argc 0 set argv {} lappend auto_path [pwd]/modadd_Lin64 source [pwd]/Tk/CryptoArmPKCS7/mainguipkcs_svg.tcl } $
В нем нас интересуют две строки. Строка с командой lappend, добавляет путь к пакетам, которые необходимы для функционирования утилиты cryptoarmpkcs. Для добавляемых пакетов мы создали папку modadd_Lin64 в нашем каталоге ~/testCloud.
Вторая строка с командой source собственно и обеспечит запуск нашей утиоиты, загрузив модуль mainguipkcs_svg.tcl. При желании здесь можно предусмотреть выполнение еще каких-то команд и передачу параметров запускаемой утилите.
В качестве примера создадим папку для запуска утилиты train.tcl, которую я храню с конца девяностых годов прошлого столетия.
Итак, в каталоге testCloud/Tk рядом с папкой TkPool создаем папку Train, в которую кладем файл train.tcl и файл TkStartup.tcl.
Вот содержимое файла TkStartup.tcl:
$ cat TkStartup.tcl set ix [lsearch $argv -display] if {$ix >= 0} { incr ix set env(DISPLAY) [lindex $argv $ix] set argc 0 set argv {} source [pwd]/Tk/Train/train.tcl } $
Обновите в браузере страницу http://localhost:8015/cloudtk/, увидите radio-кновку Train. Выберите её и можете любоваться движением старинного паровоза.
А что делать со стартовой страницей?
Тут в принципе еще проще. В нашем тестовом каталоге testCloud появилась пустая папка htdocs и, если в ней появится файл index.tml, то именно из него будет формироваться стартовая страница проекта. В качестве примера я приведу свой файл index.tml:
$ cat index.tml [html::meta_charset "utf-8"] [html::description "TclHttpd, the Tcl Web Server, is an extensible platform for web application development."] [html::keywords Tcl TclHttpd Tcl/Tk "Tcl Web Server" "Web Server"] [html::author "Vladimir Orlov"] [mypage::header "Облачные сервисы на Tcl/Tk"] \ <script type="text/javascript"> setInterval(function () { date = new Date(), h = date.getHours(), m = date.getMinutes(), s = date.getSeconds(), h = (h < 10) ? '0' + h : h, m = (m < 10) ? '0' + m : m, s = (s < 10) ? '0' + s : s, document.getElementById('time').innerHTML = "Текущее время: " + h + ':' + m + ':' + s; }, 1000); </script> <br>Время входа на портал: [clock format [clock seconds] -format "%B %d, %Y %H:%M:%S"] <br> <br><span style="color:#7e5a25; font-size:14pt; border:2px solid #e1d4ae; background:#e8e3d4; padding:5px; margin: 0 0 0 100px"> <span id="time">Текущее время: [clock format [clock seconds] -format "%H:%M:%S"]</span> </span> <br> <br><iframe src="/cloudtk/VNC?session=new&Tk=Train" height="200" width="615" name="wait_for _download" > </iframe> <h3>Демонстрация SVG-виджетов и демо-версии сервисов</h3> <p><button class="b1" style="border-radius:5px;background-color:cyan; font-size: 18px; margin: 0 0 0 100px "><a href="/cloudtk/">Переход на демо-страницу</button> </p> </table> [mypage::footer] $
Сохраните приведенный код в файле ~/testCloud/htdocs/index.tml и обновите страницу http://localhost:8015/. Появилась новая стартовая страницу. Теперь для перехода на страницу «Tk Application» достаточно нажать на кнопку "Переход на демо-страницу". Для возврата со страницы с приложениями на стартовую страницу достаточно нажать на иконку в левом верхнем углу. Обратите внимагие, что иконка в левом углу на стартовой странице и странице с приложениями одна и та же. Это связано с тем, что она задается по умолчанию в процедуре mepage::header. В принципе достаточно в процедуру mepage::header добавить умалчиваемый параметр, который бы задавал путь к иконке. Можно, конечно, получить тело процедуры (info body mepage::header), затем его поправить и снова загрузить командой source. Но более элегантным и полезным представляется получить доступ к исходному коду CloudTk.kit. Для этого нам потребуется старкит sdx.kit, который можно получить по команде
wget https://chiselapp.com/user/aspect/repository/sdx/uv/sdx-20110317.kit
Для простоты переименуем полученный старкит:
mv sdx-20110317.kit sdx.kit
Теперь всё достаточно просто. Создаём временную папку «sourceCloudTk» для работы с CloudTk.kit, копируем в нее и sdx.kit и CloudTk.kit и переходим в созданную папку.
Выполняем команду:
/usr/local/bin/tclkit-8.6.17 sdx.kit unwrap CloudTk.kit
В данной команде unwrap это команда sdx.kit для распаковки CloudTk.kit.
После выполнения команды мы получим каталог CloudTk.vfs, в котором в папке custom находим файл mepage.tcl. В файле mepage.tcl находим процедуру «proc mepage::header» и правим в ней две строки. В заголовок процедуры (строка 37) добавляем умалчиваемый параметр img:
proc mepage::header {title {img=«/images/tclp.gif»}}
После этого правим ссылку на картинку (строка 44)
[html::cell align=left "<a href=/><img src=[set img] border=0 alt=\"Home\"><\a>"] \
Правки выделены жирным шрифтом.
После этого снова собираем (подкомандля wrap) CloudTk.kit:
/usr/local/bin/tclkit-8.6.17 sdx.kit wrap CloudTk.kit
Обновлённый CloudTk.kit копируем в ~/testCloud.
Сохраните иконку Tk-облака со страницы https://wiki.tcl-lang.org/page/CloudTk в файле ~/testCloud/htdocs/images. Возвращанмся в папку ~/testCloud и правим 6-ю строку, добавляю параметр с иконкой:
[mypage::header "Облачные сервисы на Tcl/Tk" «images/cloudtk-192x192.png»] \
После этого необходимо перезапустить сервер и убедиться, что новая иконка появилась на стартовой страницы.
Если в приложении создается несколько окон (toplevel), то они будут перекрывать друг на друга. Список доступных окон можно получить, щёлкнув по иконке в виде галочки в верхнем левом углу:

Чтобы отобразилось требуемое окно надо щёлкнуть по его имени в списке, но, фактически, отсутствует управление окнами (сменить размеры, переместить и т.п.). Но всё не так плохо.
Schelte Bron предложил простой оконный менеджер, написав при этом: «Не стесняйтесь добавлять любые улучшения непосредственно в представленный код» (Feel free to add any improvements directly to the presented code). Я последовал его совету, и добавил в его код несколько функций. Теперь можно не только перемещать окна, но и масштабировать их:

С учетом этого менеджера были также переопределены функции «wm state», lower и raise.
Оконный менеджер оформлен как пакет wm и добавлен в нашем примере к другим пакетам в папку «modadd_Lin64».
Долго думал выкладывать куда-то исходники и бинарники или не нет. Всё же решил добавить все описанное здесь в проект TkSVGWidgets в каталог FofCloudTk, в который и положил интерпретатор tclkit-8.6.17-x86-64, старкит sdx.kit, пакет оконного менеджера wn (папка wmforcloud), папку Tk с примерами и обновленный старкит CloudTk.kit.
P.S. Уже когда матариал был готов, то наткнулся на красивую статью «Забытые технологии прошлого: Tclkit, Starkit и Starpack»:

Она может быть хорошим дополнением к тому, что изложено в данной статье.
