This tutorial explains how to perform common GUI customization tasks using the GUI API, such as extending the main menu, extending the popup menu, and adding your own custom widgets.

The tutorial comes with a number of example Tcl files in demo/api/guiCustomization. You can load these Tcl files as usual Userware scripts, e.g. at application startup using SpiceVision PRO’s command-line option -userware demo/api/guiCustomization/script-name.tcl, from the File  Load Userware menu, or with the source demo/api/guiCustomization/script-name.tcl command from the GUI’s Console window.

Extending the Main Menu

A new main menu entry (along with an empty sub-menu) can be added by using the gui menu mainMenu $name -position $position command.

$name is the display text of the menu entry in the main menu - if you add an underscore character _ to the $name, the following character will be the menu entry’s keyboard shortcut in combination with the Alt key.

The optional $position argument is either a number denoting the absolute insert position of the new menu entry into the main menu, or end (the default), which puts the new menu entry right of all existing menu entries.

Note
The Help menu will always stay at the rightmost position.

Example

gui menu mainMenu _Custom -position 5

This adds a new menu entry names Custom to the fifth position in the main menu, and assigns the keyboard shortcut Alt+C.

The new main menu entry can now be populated by:

gui menu command $path $command

which adds a simple menu item with an associated command,

gui menu checkbutton $path $command $variableName

which adds a checkable menu item where the check state is stored in the given variable,

gui menu radiobutton $path $command $variableName $variableValue

which adds a radio-button style menu item,

gui menu separator $path

which adds a menu separator and

gui menu submenu $path

which adds a menu item with an empty sub-menu.

The $path argument of these commands defines the menu hierarchy into which to insert the new menu item, where the last item of $path is the name of the new menu item - again, you can use an underscore character (_) to define the keyboard shortcut.

Example

The following examples use as $command procedures that are defined in the demo/api/guiCustomization/customizeMenu.tcl script.

gui menu command              \
    {{Custom} {_Into Memory}} \
    {CustomizeMenu:intoMemory}

This adds a new menu item with the name Into Memory and with the keyboard shortcut Alt+I into the sub-menu of the Custom main menu entry and associates it with the command CustomizeMenu:intoMemory (this command is run whenever the menu item is clicked).

gui menu submenu \
    {{Custom} {_Rotate}}

This adds a new menu item with the name Rotate and the keyboard shortcut Alt+R and an empty sub-menu into the sub-menu of the Custom main menu entry.

gui menu command             \
    {{Custom} {Rotate} {_0}} \
    {CustomizeMenu:rotate R0}

This adds a new menu item with the name 0 and the keyboard shortcut Alt+0 into the Custom / Rotate sub-menu, and associates the command CustomizeMenu:rotate R0 with it.

gui menu checkbutton             \
    {{Custom} {Show _Net Names}} \
    {gui settings changed}       \
    [gui settings variable "nlv:shownetname"]

This adds a checkable menu item named Show Net Names and with the keyboard shortcut Alt+N into the sub-menu of the Custom main menu entry. The check state is reflected in the nlv:shownetname settings variable (this is a global configuration option - normally set via the Preferences dialog - that specifies if the names of nets should be shown in the Schematic view). Every time the menu item is clicked, the value of nlv:shownetname setting is flipped and the command gui settings changed is executed.

You can also perform context sensitive modifications to menu items by using the gui menu customizeEntry $path $command command. The $command is executed right after opening the corresponding menu and creating the menu item indicated by $path. $command is called with an $menuId parameter which is the current Tk menu widget; by definition, the menu entry indicated by $path is the last item of the Tk menu, so it can be referenced by the index end when calling $menuId sub-commands within $command.

Example

proc CustomizeMenu:enableIfAnInstanceIsSelected {menuId} {
    set hasInstSelection 0

    set db [gui get database]
    if {$db != {}} {
        foreach oid [gui selection get] {
            if {[$db oid type $oid] eq "inst"} {
                set hasInstSelection 1
                break
            }
        }
    }

    $menuId entryconfigure end \
        -state [expr {$hasInstSelection ? "normal" : "disabled"}]
}

# Only enable the "Custom / Rotate" sub-menu if at least one object of
# type "Inst" is selected:
gui menu customizeEntry \
    {{Custom} {Rotate}} \
    CustomizeMenu:enableIfAnInstanceIsSelected

The full example can be found in demo/api/guiCustomization/customizeMenu.tcl.

Extending the Toolbar

New toolbar buttons can be added with the gui toolbar addButton function.

Example


set window          .
set name            toggleNetNames
set imageSetName    show-net
set imageSetPattern [file join [file dirname [info script]] show-net-SIZE.png]
set position        10
set command         {CustomizeToolbar:toggleNetNames}
set tooltip         "Toggle the display of net names in the schematic view"

##
# Load an imageset. Since the file pattern is ".../show-net-SIZE.png", this will
# load 16, 24, 32, 48, 64 pixel versions from ".../show-net-16.png",
# ".../show-net-24.png", etc.
#
gui imageset load $imageSetName $imageSetPattern

gui toolbar addButton   \
    $window             \
    $name               \
    $imageSetName       \
    -command  $command  \
    -tooltip  $tooltip  \
    -position $position

proc CustomizeToolbar:toggleNetNames {} {
    gui settings set "nlv:shownetname" [expr {![gui settings get "nlv:shownetname"]}]
    gui settings changed
}

The full example can be found in demo/api/guiCustomization/customizeToolbar.tcl.

Extending the Popup Menu

New popup menu entries can be added with gui popup append $label $command ?-menuname $menuname?, where $label is the intended menu label, $command is the command to be associated with the popup menu entry ($command is appended with the list of currently selected OIDs before being invoked), and $menuname is the name of the sub-menu to create the new item in (if -menuname $menuname is not specified, the menu item is created in a sub-menu called Userware).

Example

gui popup append       \
    -menuname "Custom" \
    "Toggle Net Names" \
    {CustomizePopup:toggleNetNames}
gui popup append       \
    -menuname "Custom" \
    "Rotate +90"       \
    {CustomizePopup:rotate +R90}

proc CustomizePopup:toggleNetNames {oids} {
    gui settings set "nlv:shownetname" [expr {![gui settings get "nlv:shownetname"]}]
    gui settings changed
}

proc CustomizePopup:rotate {mode oids} {
    set db [gui database get]
    foreach oid $oids {
        if {[$db oid type $oid] eq "inst"} {
            $db orient $oid $mode
        }
    }
    gui database modified
}

This example creates two popup menu items Custom / Toggle Net Names and Custom / Rotate 90.

In order to customize the new popup menu entries (e.g. enable them only under specific conditions), gui popup customize $command can be used. This executes $command ($command is appended with the Tk menu command corresponding to the popup menu and the list of currently selected OIDs before being executed) every time the popup menu is invoked, right after it is filled with all entries.

Example

gui popup customize {CustomizePopup:customize}

proc CustomizePopup:containsInst {oids} {
    set db [gui database get]
    if {$db != {}} {
        foreach oid $oids {
            if {[$db oid type $oid] eq "inst"} {
                return 1
            }
        }
    }
    return 0
}

proc CustomizePopup:customize {menu oids} {
    # Always enable our own "Custom" popup menu item.
    $menu entryconfigure "Custom" -state normal

    # Get the "Custom" sub-menu.
    set customMenu [$menu entrycget "Custom" -menu]

    # Always enable "Custom / Toggle Net Names".
    $customMenu entryconfigure "Toggle Net Names" \
        -state normal

    # Only enable "Custom / Rotate 90" if at least one instance is
    # selected.
    set hasInst [CustomizePopup:containsInst $oids]
    $customMenu entryconfigure "Rotate 90" \
        -state [expr {$hasInst ? "normal" : "disabled"}]
}

The full example can be found in demo/api/guiCustomization/customizePopup.tcl.

Adding Custom Widgets

SpiceVision PRO has a dedicated area where custom widgets can be shown - the bottom tabs that also contain the Search window, the Infobox, etc.

Users can add their own widgets into this area using the gui window insertCustomWidget $name command, where $name is the label displayed on the newly added tab. gui window insertCustomWidget internally creates a new ttk::frame and return’s its widget path. This ttk::frame is fully customizable by the user (e.g. by adding child widgets, etc.).

The custom widget (and the corresponding tab) can be removed with gui window removeCustomWidget $name. The optional parameter -pluginNamespace can be used to call the Finit procedure in the given namespace to unload and therewith disable the plugin when the custom widget is destroyed (e.g. when the user clicks the 'x' in the widget’s tab).

Example

set name "My Custom Widget (in the bottom tab area)"
set customWidget [gui window insertCustomWidget $name]
ttk::label  $customWidget.label     \
    -text "This is a custom widget."
ttk::button $customWidget.btnRemove \
    -text "Remove"                  \
    -command [list gui window removeCustomWidget $name]
pack $customWidget.label
pack $customWidget.btnRemove

This creates a custom widget and inserts it into the bottom tabs as My Custom Widget. The custom widget contains a label and a button that removes the widget by calling gui window removeCustomWidget.

Instead of the "bottom tab" area, a custom widget can also be inserted into an arbitrary Tab window by using the -tabwindow $tab option:

Example

set name2 "My Custom Widget (in an arbitrary tab)"
# get the Tab window where the "Schem" window lives in
set tab [gui window getTab "Schem"]
set customWidget2 [gui window insertCustomWidget -tabwindow $tab $name2]
ttk::label  $customWidget2.label \
    -text "This is a custom widget in the default Tab window."
pack $customWidget2.label

The full example can be found in demo/api/guiCustomization/customWidget.tcl.

Changing the GUI Layout

This example adds a new Cone window to the right of the default/main Schem window using the high-level API function gui window split …​:

# get the main Schem window
set schem [gui window defaultClassWindow Schem]

# create an empty Tab window right of the Schem window
set tab [gui window split $schem right]

# insert a new Cone window into the Tab window
gui window new -tabwindow $tab "Cone"

This example adds a row with new Schem, Cone and Source windows using lower level API functions:

##
# Get the main vertical pane window of the main window.
#
set mainVertical [gui window getMainVerticalPane .]

##
# Insert a new horizontal pane window into the main vertical pane.
# It is added bottom-most into the vertical pane, but above the Console and
# Messages windows.
#
set horizontal [gui window createHorizontalPane $mainVertical]

##
# Create and insert three vertical Pane windows into the horizontal Pane.
#
set vertical1 [gui window createVerticalPane $horizontal]
set vertical2 [gui window createVerticalPane $horizontal]
set vertical3 [gui window createVerticalPane $horizontal]

##
# Insert a Tab window into each of the vertical Pane windows.
#
set tab1 [gui window createTab $vertical1]
set tab2 [gui window createTab $vertical2]
set tab3 [gui window createTab $vertical3]

##
# Space the three vertical Pane windows evenly by setting the horizontal
# pane's sash positions to 1/3 and 2/3.
#
gui window setPaneSashes $horizontal {0.333 0.666}

##
# Insert new Schem, Cone, Source windows into the three Tab windows.
#
gui window new -tabwindow $tab1 "Schem"
gui window new -tabwindow $tab2 "Cone"
gui window new -tabwindow $tab3 "Source"

This example adds a new top-level window with side-by-side Schem and Cone windows at the top and a full-width Source window at the bottom.

##
# Create a new top-level window (this also creates and inserts the
# obligatory vertical pane).
#
set top [gui window createToplevel]

##
# Show it.
#
gui window modelessDialog \
    $top \
    "My Custom Top-Level Window" \
    -place "CENTER" \
    -size {800 600} \
    -onTop \
    -closeCallback "destroy $top"

##
# Get the vertical pane.
#
set vertical [gui window getMainVerticalPane $top]

##
# Create two horizontal panes.
#
set horizontal1 [gui window createHorizontalPane $vertical]
set horizontal2 [gui window createHorizontalPane $vertical]

##
# Space the horizontal panes evenly (set the vertical pane's sash
# position to 1/2).
#
gui window setPaneSashes $vertical {0.5}

##
# Create some vertical Pane windows.
#
set vertical1_1 [gui window createVerticalPane $horizontal1]
set vertical1_2 [gui window createVerticalPane $horizontal1]
set vertical2   [gui window createVerticalPane $horizontal2]

##
# Space the vertical pane windows in the first horizontal Pane evenly by
# setting its sash position to 1/2.
#
gui window setPaneSashes $horizontal1 {0.5}

##
# Create a Tab window in each vertical Pane.
#
set tab1_1 [gui window createTab $vertical1_1]
set tab1_2 [gui window createTab $vertical1_2]
set tab2   [gui window createTab $vertical2]


##
# Insert Schem, Cone, Source windows into the Tab windows.
#
gui window new -tabwindow $tab1_1 "Schem"
gui window new -tabwindow $tab1_2 "Cone"
gui window new -tabwindow $tab2 "Source"

The full example can be found in demo/api/guiCustomization/customizeLayout.tcl.