What is stored in this Tcl variable?

As recommended in this question , I wrote a little gui to take the options for the C command line program and pass them to the specified C program, which is already configured to handle them. It displays the way I wanted it to.

However, I would like to check if the values ​​stored in the variables are correct. Getting the values ​​to print gives me a lot of grief (I can't test in vivo directly due to some hardware issue). What am I missing?

  • Converting a variable name to "$" gives me "$ variableName", not the value of the variable.
  • Adding these variables to the array and calling is array get arr

    supposed to print the index and value of the array; I am getting variable names.
  • I tried the pathName parameter cget

    , but apparently -value

    it is not an option, and leaving the option away does not give me a list of valid parameters.

Here's all the code with various things that didn't work (from option # 1, which is the easiest way, and the others are just my attempts at workarounds). They all produce line-by-line errors: "cannot read" :: ": no such variable" or "" cannot read "colorimetric": no such variable ".

#!/opt/ActiveTcl-8.5/bin/wish8.5

wm title . "Gretag"

ttk::frame .f -borderwidth 5 -relief sunken -padding "5 10"

# next line part of the "puts" tests at the bottom
global colorimetric
ttk::label .f.dataLabel -text "Data Type"
ttk::label .f.colorimetricLabel -text "Colorimetric"
ttk::checkbutton .f.colorimetric -onvalue "-c" -offvalue "" -command getFilename1
ttk::label .f.spectralLabel -text "Spectral"
ttk::checkbutton .f.spectral -onvalue "-s" -offvalue "" -command getFilename2 

ttk::label .f.gretagNumLabel -text "Gretag #"
ttk::label .f.gretagLabel0 -text "1"
ttk::radiobutton .f.gretagRadio0 -variable gretagNum -value "/dev/ttyS0" 
ttk::label .f.gretagLabel1 -text "2"
ttk::radiobutton .f.gretagRadio1 -variable gretagNum -value "/dev/ttyS1"
ttk::label .f.gretagLabel2 -text "3"
ttk::radiobutton .f.gretagRadio2 -variable gretagNum -value "/dev/ttyS2" 
ttk::label .f.gretagLabel3 -text "4"
ttk::radiobutton .f.gretagRadio3 -variable gretagNum -value "/dev/ttyS3" 
ttk::label .f.gretagLabel4 -text "5"
ttk::radiobutton .f.gretagRadio4 -variable gretagNum -value "/dev/ttyS4" 

ttk::label .f.sampleSize -text "Sample Size"
ttk::label .f.samplex -text "X"
ttk::label .f.sampley -text "Y"
ttk::entry .f.x -textvariable x -width 5 
ttk::entry .f.y -textvariable y -width 5 

ttk::label .f.filterLabel -text "Filter Type"
ttk::label .f.filterLabel0 -text "D50"
ttk::radiobutton .f.filterRadio0 -variable filter -value "-d50" 
ttk::label .f.filterLabel1 -text "D65"
ttk::radiobutton .f.filterRadio1 -variable filter -value "-d65" 
ttk::label .f.filterLabel2 -text "Unfiltered"
ttk::radiobutton .f.filterRadio2 -variable filter -value "-U" 
ttk::label .f.filterLabel3 -text "Polarized"
ttk::radiobutton .f.filterRadio3 -variable filter -value "-p" 

ttk::label .f.baudLabel -text "Baud Rate"
ttk::label .f.baudLabel0 -text "4800"
ttk::radiobutton .f.baudRadio0 -variable baud -value "B4800" 
ttk::label .f.baudLabel1 -text "9600"
ttk::radiobutton .f.baudRadio1 -variable baud -value "B9600" 
ttk::label .f.baudLabel2 -text "19200"
ttk::radiobutton .f.baudRadio2 -variable baud -value "B19200" 
ttk::label .f.baudLabel3 -text "38400"
ttk::radiobutton .f.baudRadio3 -variable baud -value "B38400" 
ttk::label .f.baudLabel4 -text "57600"
ttk::radiobutton .f.baudRadio4 -variable baud -value "B57600" 

ttk::button .f.submitBtn -text "Submit" -command finish

grid columnconfigure . 0 -weight 1
grid rowconfigure . 0 -weight 1
grid .f -column 0 -row 0 -columnspan 11 -rowspan 5

grid .f.dataLabel -column 0 -row 0 -sticky we
grid .f.colorimetricLabel -column 1 -row 0 -sticky e
grid .f.colorimetric -column 2 -row 0 -sticky w
grid .f.spectralLabel -column 3 -row 0 -sticky e
grid .f.spectral -column 4 -row 0 -sticky w

grid .f.gretagNumLabel -column 0 -row 1 -sticky we
grid .f.gretagLabel0 -column 1 -row 1 -sticky e
grid .f.gretagRadio0 -column 2 -row 1 -sticky w
grid .f.gretagLabel1 -column 3 -row 1 -sticky e
grid .f.gretagRadio1 -column 4 -row 1 -sticky w
grid .f.gretagLabel2 -column 5 -row 1 -sticky e
grid .f.gretagRadio2 -column 6 -row 1 -sticky w
grid .f.gretagLabel3 -column 7 -row 1 -sticky e
grid .f.gretagRadio3 -column 8 -row 1 -sticky w
grid .f.gretagLabel4 -column 9 -row 1 -sticky e
grid .f.gretagRadio4 -column 10 -row 1 -sticky w

grid .f.sampleSize -column 0 -row 2 -sticky we
grid .f.samplex -column 1 -row 2 -sticky e
grid .f.x -column 2 -row 2 -sticky w
grid .f.sampley -column 3 -row 2 -sticky e
grid .f.y -column 4 -row 2 -sticky w

grid .f.filterLabel -column 0 -row 3 -sticky we
grid .f.filterLabel0 -column 1 -row 3 -sticky e
grid .f.filterRadio0 -column 2 -row 3 -sticky w
grid .f.filterLabel1 -column 3 -row 3 -sticky e
grid .f.filterRadio1 -column 4 -row 3 -sticky w
grid .f.filterLabel2 -column 5 -row 3 -sticky e
grid .f.filterRadio2 -column 6 -row 3 -sticky w
grid .f.filterLabel3 -column 7 -row 3 -sticky e
grid .f.filterRadio3 -column 8 -row 3 -sticky w

grid .f.baudLabel -column 0 -row 4 -sticky we
grid .f.baudLabel0 -column 1 -row 4 -sticky e
grid .f.baudRadio0 -column 2 -row 4 -sticky w
grid .f.baudLabel1 -column 3 -row 4 -sticky e
grid .f.baudRadio1 -column 4 -row 4 -sticky w
grid .f.baudLabel2 -column 5 -row 4 -sticky e
grid .f.baudRadio2 -column 6 -row 4 -sticky w
grid .f.baudLabel3 -column 7 -row 4 -sticky e
grid .f.baudRadio3 -column 8 -row 4 -sticky w
grid .f.baudLabel4 -column 9 -row 4 -sticky e
grid .f.baudRadio4 -column 10 -row 4 -sticky w

grid .f.submitBtn -column 1 -row 5 -columnspan 7 -sticky we

foreach w [winfo children .f] {grid configure $w -padx 5 -pady 5}
focus .f.colorimetric
.f.colorimetric state selected
.f.filterRadio1 state selected
.f.baudRadio1 state selected
bind . <Return> {finish}

proc getFilename1 {} {
set filename1 [tk_getSaveFile]
}

proc getFilename2 {} {
set filename2 [tk_getSaveFile]
}

proc finish {} {
.f.x insert 0 "-x"
.f.y insert 0 "-y"
# Pick one
# puts $colorimetric
# puts colorimetric
# puts "$colorimetric"
# puts $::colorimetric
# puts .f.colorimetric
# puts $.f.colorimetric
# puts $::.f.colorimetric
# puts "$::colorimetric"
exec ./gretag .f.colorimetric filename1 .f.spectral filename2 .f.gretagNum .f.x .f.y .f.filter .f.baud
}

      


Edit: I have posted all the code, not just a part, and on the next line are the various syntaxes from option # 1 that I tried to see the values ​​of the variables before they are passed to the next program, None of them work and I don't understand why or how to fix it. I hope another set of eyes will catch what's wrong.

0


source to share


6 answers


Basics of variables

As others have pointed out, the confusing notation before $ or not $ can be simplified with the following rule.

var is a reference to the variable itself, not its value

$ var gives the value held in the variable

It can get a little more confusing when you start thinking of everything in Tcl as a string (it really isn't, but close enough), so you can store the name of one variable in another and restore its value by reference.

% set foo "hi"
hi
% set bar "foo"
foo
% set $foo
can't read "hi": no such variable
% set $bar
hi

      

Here, the notation set $foo

is evaluated in a step - first $foo

it gives its value hi

, and then the expression set

(when run without a third argument) tries to return the value held in the variable hi

. It fails. Notation set $bar

follows the same steps, but this time it set

can operate on a value bar

that is equal foo

, and thus returns a value foo

that is equal hi

. (link to API "set")

Initialization

One of the problems with this script is initialization. In Tcl variables does not exist until a value is assigned. It's clear why trying set $foo

above didn't work because there was no variable hi

.

At the top of your script, you are trying to declare a variable with

global colorimetric

      

which doesn't work because you are already working in the global field. The global "has no effect unless it is executed in the context of the process body." (link to "global" API) You really need to use the command set

to initialize the variable. That's why none of your print attempts colorimetric

to proc finish

not work.

Scale

Another problem you run into in this script is scoping, specifically when mixing global and procedural / local scopes. You are correct if you initialized correctly colorimetric

then the code

puts $::colorimetric ;# print the value of the global variable colorimetric

      

would work. Another way to achieve this is with

global colorimetric ;# reference a global variable into the local scope
puts $colorimetric  ;# print the value of colorimetric in the local scope

      

My decision

I would like to present my solution. I admit that I have moved a lot of code and I will give a short explanation of what changes I have implemented to make it more concise.

#!/usr/bin/env wish

# --- default configuration --- #
array set CONF {
    colorimetric "-c"
    spectral     ""
    cfilename    "/path/to/defaultCI.txt"
    sfilename    ""
    x            0
    y            0
    gretagnum    "/dev/ttyS0"
    filter       "-d65"
    baud         "B9600"
}

# --- build the interface --- #
wm title . "Gretag"
ttk::frame .f -borderwidth 5 -relief sunken -padding "5 10"
grid columnconfigure . 0 -weight 1
grid rowconfigure    . 0 -weight 1
grid .f


ttk::label .f.dataLabel -text "Data Type: "
foreach {dtname dttag dtfile} {
    colorimetric "-c" cfilename
    spectral     "-s" sfilename
} {
    lappend mygrid [
        ttk::checkbutton .f.$dtname -text [string totitle $dtname] \
        -variable CONF($dtname) -onvalue $dttag -offvalue "" \
        -command [list getFilename $dtname $dttag $dtfile ]
    ]
}
grid .f.dataLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::label .f.gretagNumLabel -text "Gretag #: "
for {set tty 0} {$tty < 5} {incr tty} {
    lappend mygrid [
        ttk::radiobutton .f.gretagRadio$tty -text [expr $tty + 1] \
        -variable CONF(gretagnum) -value "/dev/ttyS$tty" 
    ]
}
grid .f.gretagNumLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::label .f.sampleSize -text "Sample Size: "
ttk::label .f.samplex -text "X"
ttk::label .f.sampley -text "Y"
ttk::entry .f.x -textvariable CONF(x) -width 5 
ttk::entry .f.y -textvariable CONF(x) -width 5 
grid .f.sampleSize .f.samplex .f.x .f.sampley .f.y


ttk::label .f.filterLabel -text "Filter Type: "
foreach {ftname ftval} {
    D50        "-d50"
    D65        "-d65"
    Unfiltered "-U"
    Polarized  "-P"
} {
    lappend mygrid [
        ttk::radiobutton .f.filterRadio$ftname -text $ftname \
        -variable CONF(filter) -value $ftval
    ]
}
grid .f.filterLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::label .f.baudLabel -text "Baud Rate: "
foreach {baud} {
    4800 9600 19200 38400 57600
} {
    lappend mygrid [
        ttk::radiobutton .f.baudRadio$baud -text $baud \
        -variable CONF(baud) -value "B$baud"
    ]
}
grid .f.baudLabel {*}$mygrid -sticky w ; set mygrid { }


ttk::button .f.submitBtn -text "Submit" -command submit
grid .f.submitBtn -columnspan 6 -sticky we

foreach w [winfo children .f] {
    grid configure $w -padx 5 -pady 5
}
focus .f.colorimetric
bind . <Return> submit

# --- callbacks --- #
proc getFilename {type tag file} {
    global CONF

    if {$CONF($type) eq $tag} {
        set CONF($file) [tk_getOpenFile]
        if {$CONF($file) eq ""} { .f.$type invoke }
    } else {
        set CONF($file) ""
    }
}

proc submit { } {
    global CONF

    exec ./gretag $CONF(colorimetric) $CONF(cfilename) \
        $CONF(spectral) $CONF(sfilename) $CONF(gretagnum) \
        $CONF(x) $CONF(y) $CONF(filter) $CONF(baud)
}

      

Discussion of changes

1. The first changes I made were to use options -text

in ttk::checkbutton

and ttk::radiobutton

. Of course, with an extra label for them, you can put text in front of the button, but this is non-standard and requires more code.

ttk::label .f.colorimetricLabel -text "Colorimetric"
ttk::checkbutton .f.colorimetric -onvalue "-c" -offvalue "" -command getFilename1

      

becomes

ttk::checkbutton .f.colorimetric -text "Colorimetric" -onvalue "-c" -offvalue "" -command getFilename1

      


2. Next, I used the similarities between the two buttons to abstract the creation in foreach. (I do this all the time in my Tcl code for the job.) This creates much easier code to read and allows you to add / remove / replace names and tags for widgets. This results in a slightly more, but much more stylish code.

ttk::checkbutton .f.colorimetric -text "Colorimetric" -onvalue "-c" -offvalue "" -command getFilename1
ttk::checkbutton .f.colorimetric -text "Spectral" -onvalue "-s" -offvalue "" -command getFilename2

      



becomes

foreach {dtname dttag dtcommand} {
    colorimetric "-c" getFilename1
    spectral     "-s" getFilename2
} {
        ttk::checkbutton .f.$dtname -text [string totitle $dtname] -onvalue $dttag -offvalue "" -command $dtcommand
}

      


3. The next change was to combine yours getFilename1

and getFilename2

into one procedure getFilename

. We can pass arguments to this function to determine who is calling it. I am using a command list

to generate a call for this new function. (link to list API)

I also started to combine your commands grid

in the widget code itself. It mygrid

stores a list of what to bind to each line in the GUI and then evaluates at the end of each section for GUI distribution on the fly. (grid API link)

The previous code gets the final version and becomes,

foreach {dtname dttag dtfile} {
    colorimetric "-c" cfilename
    spectral     "-s" sfilename
} {
    lappend mygrid [
        ttk::checkbutton .f.$dtname -text [string totitle $dtname] -variable CONF($dtname) -onvalue $dttag -offvalue "" -command [list getFilename $dtname $dttag $dtfile ]
    ]
}

      

Then the command grid

can be evaluated and the variable mygrid

is cleared after each use!


4. If you noticed, I also added a parameter -variable

to yours, ttk::checkbutton

and at that moment started storing the state of the button in a global variable CONF

. This is a big change.

Tk loves to pollute your global namespace and it's good practice to fight back. I usually add all of my configuration state to a global array and set that at the top of the code so that everyone can go in and change the default state of my code without breaking into it or making find / replace calls or something. Just good practice, so wherever you had a variable, I moved it in CONF

and moved it CONF

up.

array set CONF {
    colorimetric "-c"
    spectral     ""
    cfilename    "/path/to/defaultCI.txt"
    sfilename    ""
    x            0
    y            0
    gretagnum    "/dev/ttyS0"
    filter       "-d65"
    baud         "B9600"
}

      


5. I then propagated these changes throughout your code. Almost all widget creations have been susceptible to these changes. When it comes to creating a widget, this sometimes made the independent sections of code larger. But it also allowed me to remove the entire grid section by bundling that code into the widget code, as I said, significantly reducing the size and clutter of your code at the cost of additional complexity.


6. The final changes I made were related to your function code. You have a few minor bugs in your code getFilename1

and getFilename2

. The first error was what you want to call tk_getOpenFile

because I am only collecting a capture of the existing filename to pass to gretag

. (link to the 'tk_getOpenFile' API) If you use tk_getOpenFile

, the dialog will ensure that the file exists.

The second error getFilename1

is that the dialog has a button Cancel

, and if the user clicks the cancel button, the dialog returns an empty string. In this case, you need to uncheck the box ttk::checkbutton

and you need to disable the variable CONF(colorimetric)

. Or rather, you need to set CONF(colorimetric)

to a button -offvalue

. You can do this at the same time by sending a click to the current button.

proc getFilename1 {} {
set filename1 [tk_getSaveFile]
}

      

becomes

proc getFilename {type tag file} {
    global CONF

    if {$CONF($type) eq $tag} {
        set CONF($file) [tk_getOpenFile]
        if {$CONF($file) eq ""} { .f.$type invoke }
    } else {
        set CONF($file) ""
    }
}

      

In your function, finish

I renamed the function to submit

(sorry) and rewrote it to use the new variable CONF

.

Answer

Of course most of this is unnecessary. I wanted to give you some interesting Tcl / Tk code to think about and at the same time solve your problem. At this point, we should have a dictionary to answer your question.

The reason your variables weren't printed is due to initialization and scope. You should always use -variable

or -textvariable

in widgets that you will need to reference later. I usually initialize my reference variables before creating their containing widgets. So, at the top of your file, if you did,

set colorimetric "-c"
ttk::checkbutton .f.colorimetric -variable colorimetric [...]

      

Then you could do in the function finish

puts $::colorimetric

      

+5


source


You have not specified any variable in the colorimetric button. Add -sizable colorimeter to control button and then in finish you can use: puts $ :: colorimetric

Also, first set :: colorimetric to select the default. Which is easier than trying to mess with the state of the widget.



I see that the colorimetric values ​​can have the values ​​"and" -c, so I am assuming that you will use that value in the exec line. Remember that [do something yada $ :: colorimetric something] will probably not work You probably need {*} $ :: colorimetric in exec to make the argument disappear if empty.

+2


source


Here's a bit of tcl snippet - run from tclsh or wish

[nigel@rose ~]$ wish
% set foo /dev/ttys0
/dev/ttys0
% puts $foo
/dev/ttys0
% puts "$foo"
/dev/ttys0
% puts [$foo]
invalid command name "/dev/ttys0"
% puts ($foo)
(/dev/ttys0)
% puts {$foo}
$foo
% 

      

Quoting in Tcl:

  • ""

    (double quotes): evaluate substitutions ($ variable)

  • {}

    {Escaping brackets): treat the entire string as a literal without substitution

  • []

    (Square brackets): Execute string as a wildcard command

Alternatively, you can open diagnostics in a dialog:

% set mb [tk_messageBox -message "Port is $foo" -type ok -icon info]
ok
% 

      

0


source


In your comment "# here" try adding

puts $ :: gretagNum

:: means a global variable, and the -variable parameter for widgets is always global.

0


source


I'm not sure what you want to do, if you end up printing the variable name and not the contents of the variable, use the set command as a function:

set content "hello"

set blah value

if you do:

puts $blah 

      

.. you get the content of the blah variable, which is the content.

To get the contents of a variable through blah use the following:

puts [set $blah]

      

0


source


Just remember this rule and you will always get it right:

$foo is shorthand for [set foo]

      

Try rewriting the code with [set foo] and it will work.

In particular,

$$foo  ;# not what you think

      

is replaced by

[set [set foo]]   ;# does what you think

      

0


source







All Articles