Version 1 of A little date chooser

Updated 2001-06-25 01:20:13

Richard Suchenwirth - Here's a popup toplevel that displays a given month and allows to pick a date (or cancel):


Nice. A couple of comments (that I might just implement myself when/if I get the time):

  • I'd really like to see the month name - RS: brings in i18n problems, though - month numbers are so universal...

CLN, 2001-06-22: Yes, but I thought 8.4 was going to have i18n date support in clock so either mc March or clock format with % for the month name should work. No?

  • I find partial weeks frustrating. The first week of this month should be: 27 28 29 30 31 1 2 - RS: ... but marked in a different color, maybe gray?

CLN, 2001-06-22: Yes, a different color. I'm not sure how to choose one. ''LV, 2001-Jun-22: Why not choose something Tk-ish, but use the option database so that it could be overridden.

  • It would be nice if it accomodated European weekday ordering with Saturday and Sunday at the end, not split (that is, M,T,W,T,F,S,S, not S,M,T,W,T,R,S).
  • I'd like clicking a date to select it and close the dialog. Easy: add "set date::res $date::date" to the <1> binding - RS

Sorry to whine. It really does look nice but I've thought about this a lot and have strong opinions. -- CLN


Most of this wish list has now been fulfilled in An i15d date chooser


Feel free to add in your changes - that's one of the nice things about the wiki. In particular, if you are going to add in the month names, perhaps you will add them in a way that permits one to configure the language used to convey the months? Configuration is what I will probably be thinking about (after I think about what it will take to build a parallel widget for selecting time.

Unfortunately, my coding skills are such that one should not hold their breath waiting on me to write the time widget.


 namespace eval date {
    option add *Button.padX 0
    option add *Button.padY 0
    proc choose {} {
        variable month; variable year; variable date
        variable canvas; variable res
        variable day
        set year [clock format [clock seconds] -format "%Y"]
        scan [clock format [clock seconds] -format "%m"] %d month
        scan [clock format [clock seconds] -format "%d"] %d day
        toplevel .chooseDate -bg white
        wm title .chooseDate "Choose Date:"
        frame .chooseDate.1
        entry .chooseDate.1.1 -textvar date::month -width 3 -just center
        button .chooseDate.1.2 -text ^ -command {date::adjust 1 0}
        button .chooseDate.1.3 -text v -command {date::adjust -1 0}
        entry .chooseDate.1.4 -textvar date::year -width 4 -just center
        button .chooseDate.1.5 -text ^ -command {date::adjust 0 1}
        button .chooseDate.1.6 -text v -command {date::adjust 0 -1}
        eval pack [winfo children .chooseDate.1] -side left \
                -fill both
        set canvas [canvas .chooseDate.2 -width 160 -height 160 -bg white]
        frame .chooseDate.3
        entry .chooseDate.3.1 -textvar date::date -width 10
        button .chooseDate.3.2 -text OK -command {set date::res $date::date}
        button .chooseDate.3.3 -text Cancel -command {set date::res {}}
        eval pack [winfo children .chooseDate.3] -side left
        eval pack [winfo children .chooseDate]
        display
        vwait ::date::res
        destroy .chooseDate
        set res
    }
    proc adjust {dmonth dyear} {
        variable month; variable year; variable day
        set year  [expr {$year+$dyear}]
        set month [expr {$month+$dmonth}]
        if {$month>12} {set month 1; incr year}
        if {$month<1} {set month 12; incr year -1}
        if {[numberofdays $month $year]<$day} {
            set day [numberofdays $month $year]
        }
        display
    }
    proc display {} {
        variable month; variable year
        variable date; variable day
        variable canvas
        $canvas delete all
        set x0 20; set x $x0; set y 20
        set dx 20; set dy 20
        set xmax [expr {$x0+$dx*6}]
        foreach i {S M T W T F S} {
            $canvas create text $x $y -text $i -fill blue
            incr x $dx
        }
        scan [clock format [clock scan $month/1/$year] \
                -format %w] %d weekday
        set x [expr {$x0+$weekday*$dx}]
        incr y $dy
        set nmax [numberofdays $month $year]
        for {set d 1} {$d<=$nmax} {incr d} {
            set id [$canvas create text $x $y -text $d -tag day]
            if {$d==$day} {$canvas itemconfig $id -fill red}
            incr x $dx
            if {$x>$xmax} {set x $x0; incr y $dy}
        }
        $canvas bind day <1> {
            set item [%W find withtag current]
            set date::day [%W itemcget $item -text]
            set date::date "$date::month/$date::day/$date::year"
            %W itemconfig day -fill black
            %W itemconfig $item -fill red
        }
        set date "$month/$day/$year"
    }
    proc numberofdays {month year} {
        if {$month==12} {set month 1; incr year}
        clock format [clock scan "[incr month]/1/$year  1 day ago"] \
                -format %d
    }
 } ;# end namespace date

 #------ test and demo code (terminate by closing the main window)
 while 1 {
     set date [date::choose]
     puts $date
 }

2001-06-22 RS: stepping through months does not check for day validity, so you may get dates like 2/31/1999. Fixed in proc adjust.


Arts and crafts of Tcl-Tk programming