SVG widgets Part 4

See also SVG widgets Part 1 , SVG widgets Part 2 & SVG widgets Part 3

Tcl/tk: SVG widgets. File Explorer and information Widgets. Part 4

 


 

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

This article is written by Vladimir Orlov.

The "SVG widgets for Tcl/Tk" project addresses one of the primary criticisms leveled at the graphical interfaces of applications built using standard Tk widgets: their outdated design. Transitioning from classic Tk widgets to SVG widgets requires minimal effort; I confirmed this firsthand by rewriting the GUI for the cryptoarmpkcs digital signature utility. Distributions of this utility (named cryptoarmpkcs_svg) for Linux64 and Win64 platforms are available in the project repository:

This screenshot showcases the key features of the SVG widgets project, including the variety of widgets, the design of buttons and frames, and widget fill styles. You can run this example in the demo (using the "SVG-demo and TkConsole" button at the beginning of the article) by clicking the "Examples" button and selecting "SVGpkcs7" from the menu. It also features a scrolling text display reading «PKCS7 Utility (Public-Key Cryptography Standarts#7)». There is animation as well - rotating tokens; if they aren't spinning, simply click on the cyan-colored border.

However, not everything was perfect. When developing a GUI, one often needs to use a file browser to select files or folders (via tk_getSaveFile, tk_getOpenFile, or tk_chooseDirectory). While Windows and OS X launch their respective native file browsers, Linux platforms load the Tcl/Tk browser. What is the issue with this browser? It lacks basic file and folder operations, such as creating, deleting, or renaming items.

Consequently, a file browser based on SVG widgets was developed, resulting in the `tkfe_svg` package, which was subsequently added to the `svgwidgets` project:

 
# Loading the tkfe_svg package
package require tkfe_svg
# Replacing the native file explorer with tkfe_svg
FE::fe_rename
 
Now, when invoking a file dialog (tk_getSaveFile, tk_getOpenFile, or tk_chooseDirectory), the dialog GUI from the tkfe_svg package will be loaded.
To undo the action of the FE::fe_rename operator, simply execute the FE::fe_restore command.
The tkfe_svg package contains three operators: ::FE::tk_getopenfile, ::FE::tk_getsavefile, and ::FE::tk_choosedirectory. If the ::FE::fe_rename operator is not used, these commands are called directly.

In addition to options similar to those of the native Tcl/Tk file dialog, the tkfe_svg file dialog offers extra options. These allow you to specify, for example, the dialog's width (-width) and height (-height), or to arrange folders and files in separate windows (using the "-sepfolders 1" option) for the ::FE::tk_getopenfile and ::FE::tk_getsavefile operators.

 
# Loading the tkfe_svg package
package require tkfe_svg
# Replacing the native file explorer with tkfe_svg
FE::fe_rename /> #Launch the file explorer to select a file.
tk_getOpenFile -width 10c -height 7c -sepfolders 1
#or
#Restoring the native file explorer
#::FE::fe_restore
::FE::tk_getopenfile -width 10c -height 7c -sepfolders 1

You can also configure the file explorer using the "Go configure" button; clicking it opens an intuitive configuration menu:

File manager settings are saved upon exit. To restore the default settings, you can execute the ::FE::fe_restore command.

While discussing SVG widgets, we somehow overlooked information widgets—even though they appeared on your screen if you viewed the demo examples:

Information widgets are SVG widgets of the `mbutton` class, created using one of the following commands:

mbutton new <window-ID> [-type <type>] [args]

or

mbutton create <object-ID> <window-ID> [-type <type>] [args],

where <type> takes one of the following values: yesno | msg | left | right | up | down | none.

Thus, there are seven types of information widgets: yesno, msg, left, right, up, down, and none. By default, a widget of type yesno is created.

The following example demonstrates how yesno and msg type SVG widgets are created and how they work:

 

package require svgwidgets
set textanchor [list nw n ne]
set mtype [list yesno msg yesno]
set txt "\nAre you sure\n"
append txt "sure your choice is correct\n"
append txt "?\n"
lassign {1 0 0} i wm hm
set lmsg [list ]
lassign {8 1.0 100 3 2} fsize fopaci x0 grad wstr
foreach type $mtype {
    set tektxt "[set txt](window: .mb$i)"
    set msg [mbutton new .mb$i -place window -type $type -fontsize [expr {$fsize * $i}] -text $tektxt -strokewidth [expr {$wstr * $i}]]
   set i1 $i
   if {$i1 != 1} {
       set i [expr {$i - 0.9}]
   }
   $msg config -fillopacity [expr {$fopaci / $i}] -fillnormal gradient[set grad]
   lappend lmsg $msg
   set i $i1
   incr i
   incr grad
}
set i 1
set mend [list]
foreach m $lmsg {
   after 0 lappend mend \".mb[set i] \[$m place -x $x0 -y 100] \"
#Strange comment "
   update idletasks
   after 100
   lassign "[winfo width .mb[set i]] [winfo rootx .mb[set i]]" mw mr
   set x0 [expr {$x0 + $mw + $mr + 40}]
   incr i
}
foreach w ".mb1 .mb2 .mb3" {
   if {[winfo exist $w]} {
      tkwait window $w
   }
}
puts "Result: [lsort $mend] "

If you run this example in the demonstration window, you will get the following image as output:

After the "Yes" or "No" button is clicked on each widget, information indicating which button was clicked in which window will be printed to the console:

Result: {.mb1 yes } {.mb2 yes } {.mb3 no }

The appearance of information widgets—like that of all other SVG widgets—can be customized by specifying various options. For instance, widget stroke properties are defined using the options `-stroke` (stroke color), `-strokewidth` (stroke thickness), and `-strokeopacity` (stroke opacity, ranging from 0.0 to 1.0). Corner rounding is controlled by the `-rx` option. Font characteristics are set via the options `-fontsize` (font size), `-textstroke` (text outline color), and `-textfill` (font color).

Information widgets of the types *left*, *right*, *up*, *down*, and *none* can have a "tongue" (the `-tongue` option)—a feature we already encountered when examining the *cmenu* class widget:

Here is the code for this demonstration:

package require svgwidgets
set textanchor [list nw n ne]
set mtype [list down up left right none]
set txt "Information for you.\n"
append txt "Click me and\nthe widget will close.\n"
lassign {1 0 0 } i wm hm
set lmsg [list ]
lassign {12 1.0 100 3 2} fsize fopaci x0 grad wstr
set fsize [list 0 10 14 18 22 24]
foreach type $mtype {
    set txt1 "[set txt](Type: $type)"
    set tfont [lindex $fsize $i]
    set msg [mbutton new .mb[set i] -place window -type [set type] -fontsize $tfont -text $txt1 -strokewidth [expr {$wstr * $i}]]
    set i1 $i
    if {$i1 != 1} {
    set i [expr {$i - 0.9}]
    }
    set opacity [expr {$fopaci / $i}]
    $msg config -fillopacity $opacity -fillnormal gradient0 -stroke chocolate
    lappend lmsg $msg
    set i $i1
    incr i
    incr grad
}
set i 1
set x1 $x0
foreach m $lmsg {
    if {$i < 4 } {
    set y0 100
    } elseif {$i == 4} {
    set y0 250
    set x0 [expr {$x1 + 30}] 
    }
    after 0  $m place  -x $x0 -y $y0
    update idletasks
    after 100
    if {$i > 4} {
    break
    }
    lassign "[[lindex $lmsg $i] config -width] [winfo rootx .mb[set i]]" mw mr
    set x0 [expr {$x0 + $mw + $mr + 10}]
    incr i
    if {$i == 5} { 
    set x0 [expr {$x0 + 10}] 
    [lindex $lmsg 4] config -strokeopacity 0.75
    }
}
foreach w ".mb1 .mb2 .mb3 .mb4 .mb5" {
    if {[winfo exist $w]} {
    tkwait window $w
    }
}
puts "Ended"

Information widgets can be created either in a separate window (toplevel) or on an SVG canvas (tkp::canvas). This placement is specified when the widget is created via the `-place` parameter, which can take the value `window` or `canvas` (the default).

It is advisable to use the `place` method to position information widgets:

<ID-mbutton> place -x <X-coordinate> -y <Y-coordinate> [-in <window>]

 

To close an *mbutton* widget of the 'yesno' or 'msg' type, you must click either the "Yes" or "No" button. The returned value will be either "yes" or "no".

Complete the following example:

package require svgwidgets
set textanchor [list none nw n ne]
set mtype [list yesno msg yesno]
set txt "\nAre you sure\n"
append txt "sure your choice is correct\n"
append txt "?\n"
lassign {1 0 0 } i wm hm
set lmsg [list ]
lassign {12 1.0 10 3 2} fsize fopaci x0 grad wstr
set mret [list]
toplevel .mb -bg yellow
wm geometry .mb 700x400+200+50
foreach type $mtype {
    set ta [lindex $textanchor $i]
    set msg [mbutton new .mb.mb[set i] -place canvas -type [set type] -fontsize [expr {$fsize * $i}] -text $txt -strokewidth [expr {$wstr * $i}] -textanchor [set ta]]
    $msg config  -fillopacity [expr {$fopaci / $i}] -fillnormal gradient[set grad]
    lappend lmsg $msg
    set mw [winfo width .mb.mb[set i]]
    set mr [winfo rootx .mb.mb[set i]]
    set x0 [expr {$x0 + $mw + 40}]
    incr i
    incr grad
    update idletasks
    after 300
    set xx[set i] [$msg place -x $x0 -y 50 ]
    lappend mret [set xx[set i]]
}
puts "\nReturn: $mret\n"
destroy .mb


and you will see how it works:
 

To align text within the widget, the `-textanchor` option is used; it can take one of the values ​​`nw`, `n`, or `ne` (see the `textanchor` variable in the example).

To close other types of widgets, simply click on them.

The code below demonstrates this:

package require svgwidgets
set textanchor [list nw n ne]
set mtype [list down up left right none]
set txt "Information for you.\n"
append txt "Click me and\nthe widget will close.\n"
lassign {1 0 0 } i wm hm
set lmsg [list ]
lassign {12 1.0 10 3 2} fsize fopaci x0 grad wstr
set mret [list]
toplevel .mb -bg yellow
wm geometry .mb 700x400+200+50
cbutton create fr .mb.fr -type frame -bg gray80 -fillnormal gradient2
pack .mb.fr -fill both -expand 1
update
after 300
set fsize [list 10 14 18 22 24 28]
foreach type $mtype {
    set txt1 "[set txt](Type: $type)"
#    set msg [mbutton new .mb.mb[set i] -place canvas -type [set type] -fontsize [expr {$fsize * $i / 2.0}] -text [set txt1] -strokewidth [expr {$wstr * $i}]]
    set msg [mbutton new .mb.mb[set i] -place canvas -type [set type] -fontsize [lindex $fsize $i] -text [set txt1] -strokewidth [expr {$wstr * $i}]]
#    $msg config  -fillopacity [expr {$fopaci / $i}] -fillnormal gradient[set grad]
#    $msg config  -fillopacity [expr {$fopaci / $i}] -fillnormal cyan
    $msg config  -fillopacity [expr {1.5 * $fopaci / $i}] -fillnormal gradient0
    lappend lmsg $msg
    set mw [winfo width .mb.mb[set i]]
    set mr [winfo rootx .mb.mb[set i]]
    set x0 [expr {$x0 + $mw + 40}]
    incr i
    incr grad
    update idletasks
#    after 300
    set xx[set i] [$msg place -x $x0 -y 50 -in .mb.fr ]
    lappend mret [set xx[set i]]
}
puts "\nReturn: $mret\n"
fr destroy
destroy .mb

 

In conclusion, a new version of the tkpath package — a core package for SVG widgets—has been released. Version 0.4.3 of tkpath (`package require tkpath 0.4.3`) includes the `tkp::loupe` function, making it possible to avoid using the treectrl package when using the svgwidgets package:

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