Properties editor

Arjen Markus (1 july 2005) I am not sure whether the name "properties editor" is the official name for a widget like this, but given the context in which I have seen it in use, I'd say it is pretty close. The idea is that some entity in a program has a bunch of properties and rather than designing a specific dialogue/window to view and change the values of these properties, you simply use a generic one. The only thing you need as a programmer is the names of these properties, supposedly descriptive and informative to the actual user, and the types of these properties, an arbitrary string, a file name, ...

So, when I had seen this type of widget for the n'th time, I thought: I can do this in Tk, can't I? The script below is the result of about half an evening at the keyboard and screen. It is not perfect, it does not classify as a (meta-)widget, but in principle it is useful.

Enjoy and improve!

(Oh, yes, some statements are more complicated than they need to be, but they are internal, so I do not care too much :))

Things to improve:

  • Add a scrollbar
  • Make sure the names of the variables are lined up (columns of equal width for all frames)
  • Make sure the names of the variables are justified to the left
  • More variable types
  • Turn it into an actual meta-widget

Things puzzling:

  • The [grid] geometry manager does not always comply to the sticky option - or at least that is what I saw with Tcl/Tk 8.4.1 (I know, oldish ;)) (Peter Spjuth helped with this one: I needed to allow the grid's columns to expand. Just a note, as below I use a spinbox instead)
  • The width of the widgets inside this "editor" must be carefully controlled, otherwise you get different results with different platforms.

 # properties.tcl --
 #    Simple properties editor
 #

 # PropertiesColor --
 #    Select a new color
 #
 # Arguments:
 #    wcolor       Name of the label that gets the color
 #    var          Name of the variable associated with it
 # Result:
 #    None
 #
 proc PropertiesColor { wcolor var } {
     set color [uplevel #0 [list set $var]]
     set color [tk_chooseColor -initialcolor $color -parent $wcolor]
     if { $color != "" } {
         $wcolor configure -bg $color
         uplevel #0 [list set $var $color]
     }
 }

 # PropertiesShow --
 #    Show or hide a frame
 #
 # Arguments:
 #    wcheck       Name of the checkbutton
 #    wframe       Name of the frame
 # Result:
 #    None
 #
 proc PropertiesShow { wcheck wframe row } {
     set var [$wcheck cget -variable]
     if { [uplevel #0 [list set $var]] == 1 } {
         grid configure $wframe -row $row -sticky news
     } else {
         grid forget $wframe
     }
 }

 # propertiesEditor --
 #    Create a new window to edit properties
 #
 # Arguments:
 #    widget       Widget name
 #    properties   Definition of the properties
 #    valuesName   Name of a (global) variable containing the values
 # Result:
 #    The widget name
 #
 proc propertiesEditor { widget properties valuesName } {
     upvar #0 $valuesName values

     set group 0
     set row   0
     set count 0
     set w     [frame $widget]
     set wg    $w
     foreach {type name} $properties {
         switch -- $type {
         "group" {
             set values($name) 1
             set wt [checkbutton $widget.t$group -text $name \
                                 -variable "${valuesName}($name)" \
                                 -offrelief   flat \
                                 -overrelief  flat ]
             set wg [frame $widget.w$group -relief solid -bd 2 \
                        -bg white]
             grid $wt -sticky w
             grid $wg -sticky news
             incr group
             incr row
             $wt configure -command [list PropertiesShow $wt $wg $row]
             incr row
             set count 0
         }
         "string" {
             set wl [label $wg.l$count -text $name -fg black -bg white]
             set we [entry $wg.e$count -textvariable "${valuesName}($name)" ]
             grid $wl $we - -sticky w
             incr count
         }
         "logical" {
            #set wc [checkbutton $wg.c$count -text $name \
            #   -variable "${valuesName}($name)" -justify left]
            #set dummy [label $wg.l$count -text " " ]
            #grid $wc $dummy -sticky news ;# Stickiness does not work as I expected

             set wl [label $wg.l$count -text $name \
                        -fg black -bg white]
             set ws [spinbox $wg.s$count -values "True False" \
                        -textvariable "${valuesName}($name)" \
                        -fg black -bg white]

             grid $wl $ws - -sticky w
             incr count
         }
         "color" {
             set wl [label $wg.l$count -text $name -fg black -bg white]
             set wc [label $wg.c$count -text \
                        [string repeat " " 20] -bg $values($name)]
             set wb [button $wg.b$count -text "Choose" \
                        -command [list PropertiesColor $wc "${valuesName}($name)"]]
             grid $wl $wc $wb -sticky w
             incr count
         }
         }
     }
     return $w
 }

 # main --
 #     Testing the above procedures
 #
 set valuesx(name)         "My name"
 set valuesx(address)      "My address"
 set valuesx(value)         0
 set valuesx(myfavourite)   red
 set props {group Personalia
               string name
               string address
            group Data
               logical value
               color myfavourite
           }

 set w [propertiesEditor .w $props valuesx]
 pack $w -fill both

TV jul 19 05 Perhaps point to LemonTree.