SVG-demo and TkConsole

By clicking on the button, an interactive demo is displayed in a new browser tab using CloudTk.

In memory of Mats Bengtsson

Introduction

Few people do not recognize the convenience of tcl/tk in gui development. Moreover, it is tk called Tkinter, and not something else, that is directly integrated into Python, and into many other languages. But as soon as you show an application in which the gui is developed in tk, you can immediately hear — again, this poor, primitive, at best outdated interface. And here I agree with these critics. There have been many attempts to improve the presentability of tk widgets (in addition to ttk widgets), some of which can be viewed here. But even they look a little pale against the background of the user interface on mobile phones, qt or gtk.

My expectations related to the release of tcl/tk-9.0 were also not fulfilled in terms of the appearance of the widgets.

And since I'm a tcl/tk fan, I really want to fix this situation. It is clear that this problem can be solved by using SVG‑graphics.

Mats Bengtsson
Mats Bengtsson

Support for SVG‑graphics in tcl/tk is implemented through the tkpath package, authored by Mats Bengtsson.

But when I wanted to learn more about this man, I was met with the sad news that Mats Bengtsson, the author of the tkpath project, had left our mortal world on November 29, 2008. That's what his friend Mari Lundberg wrote about him:

Mats Bengtsson, the founder of Coccinella, passed away on November 29, 2008. He was born in 1959 and had a very successful career in academics and specifically in his field of interest, theoretical physics. Early in school he distinguished himself particularly in mathematics and he even produced his own math books in primary school when his teachers were not able to help him further on. He graduated at Lund university as an engineer, continued as a research student in theoretical physics and presented his doctoral thesis, “Parton showers in a phenomenological context”, at the age of 28 years. He spent a post‑doc period in Germany (Aachen), but previously he had also been at the Cern laboratory.

After his exams he started to work at ABB for a short period of time, before he ended up in Linköping where he received a research position at FOI, the Swedish Defence Research Agency. In Linköping he and his partner Mari stayed for quite a few years. During his entire life Mats had several hobbies such as gliding and model railroading. Unfortunately, Mats was struck by a chronic condition during the last 15 years of his life, but was able to work hard in periods on his special project, the Coccinella program which is an interactive communicative Internet program. He initiated and developed this computer program and released it as open‑source on the Net so others may contribute to the program’s development and refinement over time. Hopefully Coccinella will remain and be continuously upgraded in Mats’ spirit and visions.

It was the disease that led Mats Bengtsson from particle physics to programming. This is how his colleague at the FOI (Swedish Defense Research Agency) wrote about it:

Nowadays, I rarely look into the world of Tcl, but in fact, it was I who introduced Mats to Tcl/Tk one day by lending him my Ousterhoot book. I used to be Mats' work partner at FOI. It was at a time when he was already experiencing severe pain and tension in his neck, and no doctor could help him. His research in mathematics and physics was extremely theoretical and demanding. So he took up programming, which until then had been just a tool for him.

I have to admit that I was skeptical when he talked about his idea of shared and distributed boards. There was no way this could be implemented by a single novice programmer. But it happened.

And thiscanonly be explained by Mats' extraordinary talent.

John Ousterhout
John Ousterhout

Surprisingly, the fact is that the author of tcl/tk, John Ousterhout, also came to programming from physics, from quantum physics.

Ousterhout himself recalled:

"… after half a year of contemplating the equations of quantum physics running from page to page, I realized that the only attraction in this science for me was the opportunity to tinker with computers in the laboratory...".

Look at how similar they are in their cheerfulness.

I tested the capabilities of the tkpath package while integrating vector graphics into the tksvgpaint editor. Drawing on this experience, I decided to develop SVG widgets for Tcl/Tk. In addition to tkpath, the project required the treectrl package, which includes a “loupe” function. This function captures a snapshot of any screen area and allows for scaling it‑hence the name “loupe”. The fon method for each SVG widget class is implemented based on this function; the role of this method will be discussed later. A new version of the tkpath package has recently been released. Version 0.4.3 of tkpath (package require tkpath 0.4.3) includes the ::tkp::loupe function, making it possible to eliminate the dependency on the treectrl package when using the svgwidgets package:

if {[catch {package require tkpath}]} {
    package require tko
}
if {[info command ::tkp::loupe] == ""} {
    package require treectrl
}

Version 0.4.3 of the tkpath package was made possible thanks to support from Ashok P. Nadkarni.

Ashok P. Nadkarni author of book “The Tcl Programming Language” and the following software:

— tcl‑sfe Single file executables and build system;

— tcl9migrate Package to help migrate Tcl 8 scripts to Tcl 9;

— TWAPI Tcl Windows API;

— CFFI Package C Foreign Function Interface extension;

— WiTS Windows Inspection Tool Set;

— Woof! Web Oriented Object Framework;

— Ruff! Document generation using introspection;

— lg2 Tcl bindings to libgit2;

— TArray Typed array extension;

— Xtal A language built on Tcl for type arrays.

This is far from a complete list of the software it supports.

As for the tkpath package, the immediate plans include adding support for the ‑filloverstroke option for all graphical objects created on the canvas:

This example currently works only with the test package:

package require tkpath 0.4.4a
tkp::canvas .tkp -bg yellow
pack .tkp -fill both -expand 1
lassign "40 200 40 200" x0 x1 xs xe
for {set i 1} {$i < 5} {incr i}  {
    set obj$i [.tkp create prect $xs 40 $xe 160 -stroke {} -strokewidth 20 -fill cyan -stroke red]
    .tkp create ptext $xs 40 -textanchor nw -text "fill over stroke" -fontsize 20
    incr xs $x1
    incr xe $x1
}
.tkp itemconfigure $obj1 -stroke {}
.tkp itemconfigure $obj3 -filloverstroke 1
.tkp itemconfigure $obj4 -fillopacity 0.8 -strokeopacity 0.5

The next step is to add a gradient fill to the stroke (the ‑stroke option) of the graphic elements:

The result was the creation of the svgwidgets pet project. Transitioning from classic widgets to SVG widgets requires little effort. I confirmed this firsthand when rewriting the GUI for the cryptoarmpkcs digital signature utility:

Demonstration of SVG widgets in CryptoARM PKCS
Demonstration of SVG widgets in CryptoARM PKCS

You can explore the capabilities of the svgidgets package by visiting the demo page and clicking the “SVG‑demo and TkCon” button at the beginning of the article:

Demonstration of SVG widgets in the cloud
Demonstration of SVG widgets in the cloud

I would like to express my gratitude to Jeff Smith, who kindly offered to host a demonstration of the SVG widgets on his website.

A mere glance at a computer or tablet screen‑or at the screenshot shown here‑is enough to see that SVG widgets look more polished than standard Tk widgets. This applies to everything from button shapes and drop‑down menus to the frames themselves, and much more. I should note right away that SVG widgets do not replace standard Tk widgets but merely complement them. For example, click on the house icon, and an information window will appear in the form of a callout:

Callout
Callout

To change the font in the scrolling text (“Tcl/Tk. SVG‑widgets. Examples”), click the “Change font” button and select the font you prefer (just keep in mind that these are vector fonts):

Change font
Change font

When selecting a font, pay attention to the “fill over stroke” option, without overlooking the others. After selecting the font, click the “Accept” button, and the font in the scrolling text will change:

New font
New font

By the way, if you hover your cursor over the scrolling text and right‑click, an “About” context menu will appear, allowing you to view some brief information.

To change the general background, use the “Change background” button.

To resize the main window, use the “Window resize” checkbox and resize the window as you normally would. The SVG widgets will resize along with the window. Indeed, most of the icons in the demo are SVG icons (.svg) rather than raster images (.jpg,.png, etc.). To change the interface language, click either the flag of the country corresponding to the desired language or the appropriate radio button (Russian/English). We will save the “TkCon console” button for last. Clicking the “Examples” button opens a drop‑down menu listing examples featuring SVG widgets:

List of examples based on SVG widgets
List of examples based on SVG widgets

To start, go through all the examples, beginning with SVGbuttons (the top menu item):

SVGbuttons example
SVGbuttons example

The example invites you to simply click the buttons. Each click triggers the appearance of an information widget.

Pay special attention to the second SVGcanvas example, in which all SVG widgets are placed on a single canvas:

SVGcanvas example
SVGcanvas example

Clicking the “Update gradient” button invokes the “::gengrad::generateGradient” utility, which allows you to create a new gradient fill and change the fill in the foreground of the example:

New gradient fill
New gradient fill

Let us also consider the SVGtkmenu example, which demonstrates a menu based on the svgwidgets package:

SVGtkmenu example
SVGtkmenu example

Now that we have looked at the examples, it is time to return to the main window and use the “TkCon console” button:

TkCon console example
TkCon console example

The tk console window that appears will be a working tool that will help you get acquainted with SVG widgets. In the svgwidgets project on github you can also find a version of the graphical interpreter tclexecomp both for linux64 (folder tclexexcomp902), compiled from the source code tcl/tk-9.0.4, and a version of the interpreter based on tcl/tk-8.6 for the Linux64 and Win64 platforms (folder tclexecomp200). These interpreters, as well as the console in the CloudTk module, contain all the necessary packages for working with SVG widgets.

And the first command we will execute in the tkcon console is the command to load the svgwidgets package:

package require svgwidgets

Here you can also find out the version of the loaded tkpath package:

package version tkpath

If you are using the cloud console, it will be version 0.4.3.

Next, we will create the.firstlesson window, in which we will place our first SVG widget:

toplevel .firstlesson -bg yellow
#Create an SVG widget
set but [cbutton new .firstlesson.b1 -type round -text "first widget"] 
#and place it in the created window
wm geometry .firstlesson 300x150
pack [$but canvas] -in .firstlesson -padx 2c -pady 1c -fill both -expand 1

Let's look at the gradient fills available on the canvas of the created button:

[$but canvas] gradient names 
#gradient2 gradient0 gradient1

Let's select one of these gradient fills to set as the fill for the created button in its normal state (the ‑fillnormal option):

$but config -fillnormal gradient2
The First Lesson
The First Lesson

Yes, and look at how the button behaves when resizing the.firstlesson window.

For now, let’s destroy all the svg widgets and the.firstlesson window created in this example:

$but destroy
destroy .firstlesson

and let's move on to exploring the svgwidgets package.

SVG buttons, frames, and menus

All widgets in the svgwidgets package are grouped into five classes: cbutton, ibutton, mbutton, cmenu, and cframe. With the benefit of hindsight, one might argue that there are too many classes‑or perhaps too few‑but for now, this is the arrangement. Things may change in the future. It is already apparent, for instance, that some classes essentially duplicate one another, but we will leave them as they are for the time being. The name of the canvas hosting the SVG widget is generated in the same way as for standard Tk widgets, and it always begins with a dot.

Creating SVG widgets differs very little from creating standard widgets in Tcl/Tk. If we recall how objects of a particular class are created in Tcl/Tk object‑oriented programming, we find that there are two methods. The difference between them is that, in the first method, the identifier of the object being created is assigned by the class constructor:

<class name> new <svg-canvas name> [object options]

In the second method, the programmer assigns an identifier to the object at their own discretion:

<class name> create <object identifier> <SVG canvas name> [object options]

In both cases, the constructor returns the identifier of the created object.

The destroy method is used to destroy the object:

<object identifier> destroy

SVG widgets can be created individually on separate canvases and then displayed using one of the layout managers (pack, grid, place), or a group of widgets (or all of them) can be created on the same canvas.

When creating an SVG widget, the class constructor checks whether the specified SVG canvas exists. If the canvas does not exist, it is created.

When placing multiple SVG widgets on a single canvas, layout managers are not used; their placement is determined by the -x and -y options, which specify the coordinates of the top‑left corner of the widget's bounding rectangle. By default, coordinates are specified in pixels. To switch from pixels to other units of measurement, append one of the following letters to the coordinate value:

  • c — centimeters;

  • i — inches;

  • m — millimeters;

  • p — points (1/72 inch).

Naturally, one of the layout managers is used to position the canvas containing the SVG widgets.

So, after loading the svgwidgets package, we create, for example, a window named “.win” with dimensions of, say, 7 cm by 9 cm:

toplevel .win -bg yellow
wm geometry .win [winfo pixels .win 7c]x[winfo pixels .win 9c]

It is worth noting that the accuracy with which the window dimensions match the specified width and height values ​​depends on the scaling factor (tk scaling), which was 1.33333 on the CloudTk cloud computer at the time of writing.

You can take the example code directly from this article and paste it into the TkCon console running in the CloudTk cloud for execution. Copying and pasting code from the article into the TkCon console is a three‑step process.

In the first step, the selected code is copied (Ctrl+C) from the article to the shared clipboard:

In the second step, switch to the tab where the TkCon console is running and paste (Ctrl+V) the code‑previously copied to the shared clipboard‑into the noVNC clipboard (accessible via the CloudTk control tab on the left side of the window):

The copied code is immediately placed in the shared clipboard of the server running the demonstration. Moreover, the code in the clipboard can be edited.

In the third step, all that remains is to paste (Ctrl+V) the code from the clipboard into the “TkCon” console, where it will execute immediately (a yellow‑filled “win” window appears in the left corner of the screen):

When developing a GUI based on SVG widgets (or even standard Tk widgets), I would recommend placing a frame (e.g., .frame1) over the window that completely covers it (e.g., place.frame1 ‑in. ‑relwidth 1.0 ‑relheight 1.0). This approach offers numerous advantages; for instance, you can use the lower command instead of forget, and the raise command instead of repeatedly using layout managers like place, pack, or grid.

Following this recommendation, let’s start by creating an SVG frame with the ID frame1 on the SVG canvas .win.fr1 and placing it within the created window .win:

#Creating a frame (-type frame) based on the cbutton class
cbutton create frame1 .win.fr1 -type frame -strokewidth 1m -stroke cyan -rx 0 -fillnormal white 
#You can create an SVG frame using the cframe class:
#cframe create frame1 .win.fr1 -type frame  -strokewidth 1m -stroke cyan -rx 0 -fillnormal white 
#Placing an SVG frame in a window
pack [frame1 canvas] -in {.win}  -fill both -expand 1
#Or
#pack .win.fr1 -fill both -expand 1

Alternatively, instead of a standard SVG frame, you can create an SVG frame with a title‑after first destroying the previously created frame, frame1:

frame1 destroy
cframe create frame1 .win.fr1 -type clframe -text {SVG frame with a title} -strokewidth 2m -stroke cyan -rx 0 -fillnormal white -fillbox cyan -fontsize 4m
#Styling a frame's title (boxtext method) (default color: -fillbox)
frame1 boxtext -ipadx 1m -ipady 2m -stroke chocolate -strokewidth 1 -rx 2
frame1 pack -in .win -fill both -expand 1

If the ‑rx option is set to 0 (zero), the frame will be rectangular. If no border is required for the SVG frame, simply set the options ‑stroke {} and ‑strokewidth 0. The fill of the SVG widget is determined by the ‑fillnormal option; the fill for the SVG frame “frame1” can be solid, gradient, or transparent (frame1 config ‑fillnormal {}).

Now, on the same canvas .win.fr1‑which already contains the SVG frame frame1‑we will create several SVG widgets of the cbutton class (buttons) arranged vertically.

In this case (when placing multiple SVG widgets on a single canvas), you will need to use the ‑x and ‑y options to specify the location of each SVG widget on the canvas:

#Vertical spacing between widgets is 15 millimeters
set y0 [winfo pixel .win.fr1 15m]
#Vertical coordinate for the first button
set y1 [winfo pixel .win.fr1 5m]
#Let's create five SVG buttons: "rect, round, ellipse, square, circle"
#Clicking a button prints its ID.
foreach tbut "rect round ellipse square circle " {
        cbutton create b$tbut .win.fr1 -type $tbut -x 2c -y $y1 -text "ID:b$tbut" -fontsize 4m -command "puts \"The b$tbut button is pressed \""
        incr y1 $y0
}

The result of our actions will be displayed in the top‑left corner:

SVG buttons
SVG buttons

Note that when placing an SVG widget on a separate canvas, the ‑x and ‑y parameters are ignored. However, in this case, you will need to use one of the window layout managers (pack, grid, or place), for example:

#deleting the created buttons:
foreach tbut "rect round ellipse square circle " {
b$tbut destroy
}
#Creating five buttons ("rect round ellipse square circle") 
#each button is created on its own canvas
foreach tbut "rect round ellipse square circle " {
cbutton create b$tbut .win.$tbut -type $tbut -text "ID:b$tbut" -fontsize 4m  -bg white -command "puts \"The b$tbut button is pressed \""
b$tbut pack -in .win.fr1 -padx 2c -pady "0.5c "}

To place the SVG widget in the window in this example, we used the pack method. The grid and place methods can be used in a similar way; their usage mirrors that of the corresponding window layout managers.

This is the perfect moment to explain the structure of SVG widgets.

What the user sees on a computer or smartphone screen can be visualized as a multi‑layered cake which, depending on the filling, may consist of four or five layers:

SVG widgets (the topmost layer or, as the saying goes, the cherry on top) are placed on the canvas over the SVG‑image layer (if present) or the SVG‑fill layer.

SVG-widget
SVG‑widget

Both of these layers consist of an SVG rectangle with dimensions matching those of the SVG canvas. The SVG canvas is created using the corresponding class constructor with no borders (specifically, ‑borderwidth 0 and ‑highlightthickness 0). The SVG‑fill layer was introduced because the canvas itself supports only a solid‑color fill (via the ‑background option); this layer overcomes that limitation, allowing for gradient fills. Furthermore, the layer can be set to transparent (‑background {}), revealing the canvas's native background. The SVG‑image layer appears when an SVG widget‑created on a separate canvas‑is placed over other widgets. In this scenario, a screenshot of the underlying area is captured to form the SVG‑image layer. Each class includes a fon method for this purpose. Typically, this process occurs automatically during calls to the place method, window resizing, or when applying gradient fills or transparency settings to SVG widgets. It is precisely the presence of the fon method that enables the virtual transparency of SVG canvases.

In our example, we created SVG widgets of the cbutton class.

The appearance of the cbutton class SVG widget is determined by the ‑type option.

Broadly speaking, three groups of buttons can be distinguished within the cbutton class (please bear with me‑that’s just how it turned out). The first group consists of three types of classic buttons (using the ‑type <widget type> option):

  • -type rect – create a rectangular widget (default);

  • -type round – create a widget with a narrow side that has semi-circular ends;

  • -type ellipse – an ellipse-shaped widget.

These three types of widgets are of the cbutton class and are presented in the screenshot above.

To create a rectangular widget with rounded corners, you need to specify the rect type and set the corner rounding value using the ‑rx option. The SVG frame (which we encountered earlier) is also created as an object of the cbutton class with the frame type (see the example):

cbutton create frame1 .win.fr1 -type frame -rx 5m

The second group includes buttons that are functionally identical to those in the first group but have a slightly different appearance (the last two widgets in the screenshot):

  • -type circle – create a round button;

  • -type square – create a square button.

The third group includes radio and check buttons:

  • -type radio – create a radio button;

  • -type check – create a check button.

These buttons feature an additional ‑variable option, used to specify the name of the variable linked to the button (just like with standard radiobutton and checkbutton widgets). Naturally, radio buttons have a ‑value option that defines the value assigned to the variable when the button is selected (clicked).

Now you can use your mouse to move over the widgets and even click on them. You will be able to see which widgets respond to the mouse cursor or mouse button clicks, and which ones ignore the mouse.

To obtain a list of created objects of a specific class, the following command is used:

info class instances <class>

Each object possesses a specific set of properties that can be retrieved or modified by calling the config method:

<object> config [[-option [option value]]]

When the config method is called without parameters, a list of all parameters with their current values is returned. When the config method is called with a parameter, the current value of that parameter is returned. To change the value of a parameter, you can call the config method with the parameter and its new value.

You can get a list of methods that each object has by running the following command:

<object> methods

Let us return to the “layer cake” concept to examine the layers of the SVG widget. We will dissect the SVG widget with the ID frame1, located on the .win.fr1 canvas.

It should be noted at the outset that Layer 3 (SVG‑image) is absent in this example; its introduction will be discussed later.

For demonstration purposes, let's set the background color of the .win.fr1 frame to red:

.win.fr1 configure -background red	

Let us remind you that if you use the CloudTk clipboard, do not forget to clear it before entering new commands into it.

So, the .win window (this is Layer 0) was created with a yellow background (‑background yellow), and to confirm this, we will change the placement of the .win.fr1 canvas by adding padding on the top, bottom, left, and right sides (left screenshot in the illustration below):

pack configure .win.fr1 -padx 5m -pady 5m

Now, make the fill of the frame1 svg widget (Layer 4) transparent (the second screenshot from the left in the illustration below), and the fill of Layer 2 (SVG‑fill) will become visible:

frame1 config -fillnormal {}

Uncolor the SVG‑fill (Layer 2) and the native canvas fill will become visible (third screenshot from the left in the illustration below):

 frame1 config -background {}
SVG Widget Layers
SVG Widget Layers

Let's return the Layer 2 fill to its original state:

frame1 config -background yellow

The fill of the frame1 (Layer 4) svg widget itself will be replaced from transparent to gradient (see the left screenshot in the illustration below):

frame1 config -fillnormal gradient2

You can get a list of gradient fills available on the frame1 widget canvas by running the following command:

[frame1 canvas] gradient names или .win.fr1 gradient names

Now let's make rounded corners with a radius of 10 millimeters for the frame1 widget (option ‑rx) (see the left screenshot in the illustration below):

frame1 config -rx 10m 

After that, we will make the svg canvas fill for the frame1 svg widget transparent (‑background option):

frame1 config  -background {}

You can also add partial transparency (or rather opacity) to the fill of the frame1 widget:

frame1 config  -fillopacity 0.7
Layered SVG Pie
Layered SVG Pie

Looking at the two right‑hand screenshots, it becomes clear why the SVG‑image and SVG‑fill layers are needed. Their role is to coordinate the color scheme when combining svg widgets placed on different canvases.

The size of the svg widget is regulated by the options ‑width and ‑height. The outline thickness of the svg widget is set by the ‑strokewidth option, the outline color is set by the ‑stroke option, and the outline transparency is adjusted by the ‑strokeopacity option.

As for the gradient fill, how to form it is written in the documentation for the tkpath and tko packages. And the svgwidgets package has a gui utility for creating gradient fills.:

:gengrad::generateGradient [<svg canvas> <gradient ID>]

If you plan to create a new gradient based on a previously created one, then specify the identifier of the svg canvas (<svg canvas>) on which it was created and the identifier of the gradient itself (<gradient ID>) as parameters (see the SVGcanvas example in the demo):

Gradient generation
Gradient generation

The utility returns the code for generating the gradient.

There is also a gui utility tk_fontsvg for selecting an svg font.

And now we will look at the svg widget of the cmenu class, which creates a menu.

When creating a menu, you can set options that will determine its appearance:

‑fillnormal — menu fill color; ‑fontsize — font size for menu items; ‑stroke — stroke color; ‑strokewidth — stroke thickness; ‑tongue — the size of the tongue; ‑direction — sets the side (up,down, left, right) of the menu from which the “tongue” sticks out; ‑place — defines the place (canvas or window) where the menu is formed; the menu can be formed both on the canvas and in a separate window created by the toplevel command.

The ‑tongue option requires clarification. This option is set as a list of four values and by default this option looks like this:

-tongue [list 0.45 0.5 0.55 5m]

The first three numbers, whose values range from 0 to 1.0, specify the coordinates of the language on the side specified by the ‑direction parameter. The first coordinate defines the starting point, the third — the ending point on the specified side, the second point defines the coordinate of the tip of the tongue. The fourth value determines the length of the tongue. The length can be set in pixels, points, centimeters, millimeters, or inches. In the event that the tongue is not required, it is sufficient to set its length to zero (‑tongue {0.45 0.5 0.55 0}).

All this is clearly visible in the screenshot (see the SVGcanvas example in the demo):

SVG menu
SVG menu

The cmenu SVG‑widget is similar in many ways to the classic menu. The menu can contain various types of items (command, check, radio, cascade, separator). The add method is responsible for adding items to the menu:

<ID-cmenu> add [command | check | radio | separator | cascade] [args]

The add method returns the ID of the menu item. This identifier can be used, for example, to set new values for certain menu item options.

A command type item is a button of the cbutton class of the rect type. Check and radio type items are cbutton class buttons of the check or radio type, respectively. A separator type item is just a separator between items. A cascade type item is used to open a submenu. How a cascade type item works will become clear below. You can add parameters (args) corresponding to its prototype to each item, for example, “‑fillnormal cyan”.

Let's create the menu shown in the screenshot:

#Creating a SVG-menu
set menu [cmenu new .win.c -direction up]
# Adding a check-type item to the menu
$menu add check -text check1 -variable z2
#Adding a command-type item to the menu
$menu add command -text "Command" -command {puts "Enter Button Command"}
# Adding a radio-type item to the menu

$menu add radio -text radio1 -variable z1 -value 0
# Adding a radio-type item to the menu
$menu add radio -text radio2 -variable z1 -value 1
#Finishing up the menu
set mbut [$menu add finish]
#Setting the menu border color to chocolate brown
$mbut config -stroke chocolate

The place method is used to place the menu.

The ‑x and ‑y coordinates are set as options (the ‑anchor option is set to nw by default). When placing the menu for the first time, it is advisable to set the radio and check button variables to the desired value by clicking on the corresponding buttons or setting the values of the variables associated with them:

#Placing a menu at x=40 and y=100 coordinates
$menu place -x 40 -y 100 -in .win.fr1
# Setting the initial values of variables z1 and z2
set z1 1
set z2 1

As a result, we will see our menu on the screen:

Example of an SVG menu
Example of an SVG menu

You can hide the menu using the forget method:

$menu forget

This is how the context menu works using the place and forget methods (see the SVGtkmenu example or right‑click on the ticker “Tcl/Tk.SVG‑widgets. Examples.” on the main demo window):

Context menu
Context menu

Most often, the menu should appear when a button (menubutton) is clicked. To do this, it is enough to “bind” the created menu to the cbutton class button using the ‑menu option of the config method:

<cbutton ID> config -menu <cmenu ID>

When an empty menu ID is set, the button returns to its normal state.

In our example, we'll link the created menu to the brect:

# Hiding the menu
$menu forget
# Linking the menu to the brect button
brect config -menu $menu

Now, when you click the “ID:brect” the menu will toggle between appearing and disappearing.:

By the way, you don't have to click the button; you can call its invoke method instead:

$mbut invoke

You can see how to work with menus in the SVGcanvas and SVGtkmenu examples.

And remember that the destroy method is used to remove the SVG widget.

To be continued.

P. S. Please don't judge too harshly; this is my first post on Habr in English.