See also SVG widgets Part 1 & SVG widgets Part 3
)
By clicking on the button, an interactive demo is displayed in a new browser tab using CloudTk.
This article is written by Vladimir Orlov.
We already know that SVG widgets are based on the functionality of the tkpath (you can use the tko package instead) and treectrl packages. To develop a GUI using SVG widgets, you need to install the svgwidgets package, which you can download here. If you plan to use SVG files (SVG images) based on XML code, you will also need to install the svg2can package, which you can download here.
The svgwidgets project on GitHub contains a version of the tclexecomp graphical interpreter for Linux64 (folder tclexexcomp902), built from the tcl/tk-9.0.2 source code, as well as a version based on tcl/tk-8.6 for Linux64 and Win64 platforms (folder tclexecomp200).
For testing, we suggest using the TkCon cloud console from the demo example, which can be launched in a browser by clicking the "SVG-demo and TkConsole" button at the beginning of the article. After launching the demo, click the "TkCon console" button, and when the console appears, run the command "package require svgwidgets":

As a reminder, SVG widgets can be created individually on a separate canvas and then displayed using one of the layout managers (pack, grid, place), or all or a group of widgets can be created on the same canvas. When placing multiple SVG widgets on a single canvas, layout managers are not used; their placement on the canvas is controlled by the -x and -y options. Naturally, one of the layout managers is used to position the canvas containing the SVG widgets.
So, after loading the svgwidgets package, we create a «.win» window, say, 7 cm by 9 cm:
toplevel .win -bg yellow
wm geometry .win [winfo pixels .win 7c]x[winfo pixels .win 9c]
As a reminder, the accuracy of the window's dimensions relative to the specified width and height depends on the tk scaling factor, which on the CloudTk server at the time of writing was 1.33333.
The example code can be taken directly from this article and pasted into the TkCon console for execution. Copying and pasting code from the article into the TkCon console is a three-step process.
The first step is to copy the highlighted code (Ctrl-C) from the article to the shared clipboard:

In the second step, go to the tab where the TkCon console is running and paste (Ctrl-V) the copied code into the shared clipboard in the noVNC clipboard (accessible through the CloudTk control tab in the sidebar):

The copied code is immediately added to the shared clipboard of the server running the demo. Furthermore, the code in the clipboard can be edited.
In the third step, all that's left is to paste (Ctrl-V) the code from the clipboard into the TkCon console, where it will immediately execute (a .win window with a yellow fill appears in the left corner of the screenshot):

This algorithm should work in reverse as well.
When developing a GUI based on SVG widgets (though for regular TK widgets too), I would recommend placing a frame (e.g., .frame1) over the window, completely covering the window (e.g., place .frame1 -in . -relwidth 1.0 -relheight 1.0). This approach has many advantages, for example, you can use the lower command instead of the forget command, and use the raise command instead of reusing the place, pack, and grid layout managers.
Following this recommendation, let's start by creating an SVG frame with the ID frame1 on the SVG canvas .win.fr1 and place it in the newly created .win window:
#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
You can also create an SVG frame with a title instead of a regular SVG frame, after first destroying the existing frame frame1:
frame1 destroy
cframe create frame1 .win.fr1 -type clframe -text {Фрейм с заголовком} -strokewidth 2m -stroke cyan -rx 0 -fillnormal white -fillbox cyan -fontsize 5m
#Styling a frame's title (boxtext method) (default color: -fillbox)
frame1 boxtext –ipadx 1m –ipady 1m –stroke chocolate –strokewidth 1 -rx 2
If you set the -rx option to 0 (zero) when creating a frame, the frame will be rectangular. If you don't want a border for the SVG frame, you can simply set the options "-stroke {} -strokewidth 0." The fill of the SVG widget is determined by the -fillnormal option. The fill of the SVG frame "frame1" can be a solid color, a gradient, or transparent (frame1 config -fillnormal {}).
Now, on the same .win.fr1 canvas, which already contains the SVG frame frame1, let's create several SVG widgets of the cbutton class, arranged vertically.
In this case (when placing multiple svg widgets on the same 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 upper left corner:

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.fr1.$tbut -type $tbut -text "ID:b$tbut" -fontsize 4m -bg white -command "puts \"The b$tbut button is pressed \""
b$tbut pack -padx 2c -pady "0.5c "}
In this example, we used the pack method to place the svg widget in the window. Similarly, you can use the grid and place methods, which are similar to the corresponding layout managers for standard widgets.
This is the right place to talk about the structure of svg widgets. The svgwidgets package implements object-oriented classes of svg widgets based on svg canvases supported by the tkpath (tkp::canvas) or tko (tko::path) packages.
What the user sees on the computer or smartphone screen can be imagined as a layered pie, which, depending on the filling, can have four or five layers:

SVG widgets (the topmost layer, as they say, the cherry on top) are placed on the canvas over the SVG-image layer (if present) or the SVG-fill layer.
Both of these layers are svg rectangles that match the size of the SVG-canvas. The SVG-canvas is created by the corresponding class constructor without any borders (-borderwidth 0 and -highlightthickness 0).
The appearance of the SVG-fill layer is caused by the fact that the fill (-background) of the SVG-canvas can only be single-colored. The introduction of the SVG-fill layer eliminates this drawback. Now, the fill of the canvas can be not only single-colored, but also gradient. Moreover, this layer can also be transparent (-background {}), in which case the native fill of the SVG-canvas will be visible. The SVG-image layer appears when we place an SVG widget created on a separate SVG-canvas on top of any other widgets. In this case, a screenshot of the area where it will be placed is taken, and this screenshot forms the SVG-image layer. For this purpose, each class has a fon method. Typically, this is done automatically when the place method is called, windows are resized, or the gradient fill or transparency properties of SVG widgets are set. The presence of the fon method ensures the transparency of SVG-canvases.
All widgets in the svgwidgets package are grouped into five classes: cbutton, ibutton, mbutton, cmenu, and cframe. Now, after some time has passed, we can say that there are either too many or too few classes, but for now, this is the case. As time goes on, things may change. For example, it is already evident that some classes actually duplicate each other. However, we will not touch them for now. The name of the canvas for an svg widget is formed in a similar way to how it is done for classic Tk widgets, and it always starts with a dot.
Creating svg widgets is almost identical to creating classic widgets in tcl/tk. If we recall how objects of a particular class are created in object-oriented programming in tcl/tk, we will find that there are two methods. The difference between these methods lies in the fact that in the first method, the identifier of the created object is assigned by the class constructor:
<class name> new <canvas name> [object parameters]
In the second method, the object identifier is explicitly assigned by the programmer:
<class name> create <object ID> <canvas name> [object options]
In both cases, the constructor returns the ID of the created object.
When creating an SVG widget, the constructor checks whether the SVG canvas has been created before.
If the canvas has not been created yet, the SVG-fill layer is created immediately after the canvas is created.
The SVG widget itself is always created on top of the SVG-fill layer.
The destroy method is used to destroy an object:
<object ID> destroy
When an object is destroyed, the canvas on which the svg widget was created is also deleted.
In our example, we created svg widgets of the cbutton class.
The appearance of an svg widget of the cbutton class is specified by the -type option.
There are three groups of buttons in the cbutton class (don't blame me, it just happened this way). The first group consists of classic buttons of three types (option -type <widget type>):
-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 cbutton widgets are shown in the screenshot above.
If we want to create a rectangular widget with rounded corners, we can specify the rect type and set the corner radius using the -rx option.
A frame can also be created as a cbutton object with the frame type (see the example):
cbutton create frame1 .win.fr1 –type frame –rx 5m
The second group includes buttons that are identical to the first group in terms of functionality, 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 have an additional -variable option, which sets the name of the variable that is associated with the button (similar to the classic radiobutton and checkbutton). Naturally, radio buttons have an -value option that sets the value for the variable when the button is selected (clicked).
Now you can use your mouse to navigate through the widgets and even click on them. You can see which widgets respond to the mouse cursor and mouse clicks, while others ignore the mouse.
To get a list of objects of a specific class (for example, cbutton), use the following command:
info class instances <class>
Each object has a specific set of properties that can be obtained 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's return to the "layer cake" to see the layers of the svg widget. We will dissect the svg widget with the frame1 identifier located on the .win.fr1 canvas.
It should be noted that layer 3 (SVG-image) is not present in this example. We will discuss its appearance later.
For clarity, let's make the color of the .win.fr1 canvas red:
.win.fr1 configure -background red
Note. If you use the CloudTk clipboard, don't forget to clean 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 {}
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

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:

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:

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]
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 # 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:

You can hide the menu using the forget method:
$menu forget
This is how the context menu works using the place and forge methods (see the SVGtkmenu example or right-click on the ticker "Tcl/Tk.SVG-widgets. Examples." on the main demo window):

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 click on the brect button or apply the invoke method to it and look at the screen:

Now it becomes clear how the cascading menu is built. To do this, it is enough to specify the -menu parameter with the identifier of the previously created submenu when adding the cascade component to the menu.
You can see how the menu works using the SVGcanvas and SVGtkmenu examples.
In the following, we will look at working with messages and svg images.