if 0 {
MiHa 2015-05-13: This is a demo for a tcl/tk-program with all the essential GUI-elements.
It acts as a simple invoice-program with menu, entry-form and output (planned: one printed page).
All the data is just strings of text, no validation, no calculation.
With ideas and code from the following pages:
}
# InvoiceDemo09.tcl - Mike Hase - 2015-05-13 / 2015-05-21 # https://wiki.tcl-lang.org/29284 # Simple "invoice"-themed GUI-demo, with menu, form for data-entry, output to screen and printer. # (output to printer is not done yet) package require Tk set Prog(Title) "Invoice-Demo" set Prog(Version) "v0.09" set Prog(Date) "2015-05-21" set Prog(Author) "Mike Hase" set Prog(HomePage) "https://wiki.tcl-lang.org/29284" set Prog(Contact) "[email protected]" set Prog(About) "A GUI-demo (disguised as an invoice-program:)\nPlus a printer-test." #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ global data maxFNr startF1 startF2 #set FieldNames1 {"Date " Title Name Street City Phone } set FieldNames1 { Title Name Street City Phone } #set FieldNames2 { art1Nr art1qty art1desc art1price price1 } #set FieldNames2 { Article-Number Quantity Description Price Total } set FieldNames2 { Item Description Unit-Price Quantity Amount } #---+----1----+----2----+ set fNr 0 set artNr 0 set lenLb 16 set lenEn 20 #: Boilerplate-text / Company-Address: set data(Adr1) "We have no T-Shirts Inc." set data(Adr2) "Marketplace 9" set data(Adr3) "Bigcity" set data(Adr4) "Somewhere" set data(Fon) "555-4321-0" set data(Fax) "555-4321-9" set data(Mail) "[email protected]" #---+----1----+----2----+----3 # Build data-entry form: ## Todo: put this form into its own dialog #: Frame for customer-data: frame .f1 ;# -background red set field "Date" incr fNr set data($fNr) $fNr ;## Test set lb [label .f1.lb$field -text $field ] set en [entry .f1.en$field -width 10 -textvar data($fNr) ] grid $lb $en -padx 4 -pady 2 grid $lb -sticky e grid $en -sticky w set hdr [label .f1.lbHdr -text "Customer:"] grid $hdr -columnspan 2 -pady 8 foreach field $FieldNames1 { incr fNr set data($fNr) $fNr ;## Test set lb [label .f1.lb$field -width $lenLb -justify right -text $field ] set en [entry .f1.en$field -width $lenEn -justify left -textvar data($fNr) ] grid $lb $en -padx 4 -pady 2 grid $lb -sticky e grid $en -sticky w if { $fNr == 2 } { set startF1 $en } } #: Frame for article-data: # Same layout as above. Todo: place all fields for an article in one line frame .f2 ;# -background green incr artNr ;# Todo: extend to more articles set hdr [label .f2.hdr -text "Purchase:"] grid $hdr -columnspan 2 -pady 8 foreach field $FieldNames2 { incr fNr set data($fNr) $fNr ;## set lb [label .f2.lb$field -width $lenLb -justify right -text $field ] set en [entry .f2.en$field -width $lenEn -justify right -textvar data($fNr) ] grid $lb $en -padx 4 -pady 2 grid $lb -sticky e grid $en -sticky w #if { $startF2 == "" } { set startF2 $en } if { ![info exists startF2] } { set startF2 $en } } set maxFNr $fNr if 0 { ;# these buttons can be used as an alternative to the m+menu #: Frame for buttons: frame .f8 -background blue button .f8.b1 -text New -command { data0 } button .f8.b2 -text Test1 -command { data1 } button .f8.b3 -text Test2 -command { data2 } button .f8.b4 -text Show -command { showData2 } button .f8.b5 -text Print -command { printData } button .f8.b6 -text Exit -command { exit } grid .f8.b1 .f8.b2 .f8.b3 .f8.b4 .f8.b5 .f8.b6 pack .f1 .f2 .f8 -side top } pack .f1 .f2 -side top #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ # Menu: proc m+ {head name {cmd ""}} { #: Menu-creator "m+" by R.Suchenwirth and DKF, 2006-08-24, see https://wiki.tcl-lang.org/16327 # Uses the undocumented and unsupported feature ::tk::UnderlineAmpersand if {![winfo exists .m.m$head]} { foreach {l u} [::tk::UnderlineAmpersand $head] break .m add cascade -label $l -underline $u -menu [menu .m.m$head -tearoff 0] } if {[regexp ^-+$ $name]} { .m.m$head add separator } else { foreach {l u} [::tk::UnderlineAmpersand $name] break .m.m$head add command -label $l -underline $u -comm $cmd } } # GUI: define menu, bindings . configure -menu [menu .m] m+ &File &Open { Dummy "Open" } m+ &File &Save { Dummy "Save" } m+ &File ----- m+ &File &Exit exit m+ &Edit &Clear data0 m+ &Edit Testdata&1 data1 m+ &Edit Testdata&2 data2 m+ &Output &List showData2 m+ &Output &Show showData3 m+ &Output &Print printData m+ &Help &About About m+ &Help &Help Help m+ &Help ----- m+ &Help &Debug { console show } bind all <Key-F1> Help bind all <Key-F2> { console show } bind all <Key-F3> bell bind all <Key-F4> exit bind all <Key-F5> data0 bind all <Key-F6> data1 bind all <Key-F7> data2 bind all <Key-F8> showData2 bind all <Key-F9> showData3 bind all <Key-F10> printData #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ proc Dummy {txt} { #: Alert: "Function X not implemented yet" bell tk_messageBox -icon warning -title "Demo" \ -message "Function '$txt' not implemented yet." } proc About {} { #: Short info about the program global Prog set txt "$Prog(Title) $Prog(Version) - " append txt "$Prog(Date)\nby $Prog(Author) - $Prog(Contact)\n\n$Prog(About)" tk_messageBox -icon info -message $txt -title "About $Prog(Title) $Prog(Version)" } proc _Help {} { Dummy Help } ;# needs work proc Help {} { global Prog set w .help if { [winfo exists $w] } { wm deiconify $w raise $w focus $w return; } toplevel $w wm title $w "$Prog(Title) - Help" frame $w.f text $w.f.t -wrap word scrollbar $w.f.sb $w.f.t configure -yscrollcommand "$w.f.sb set" $w.f.sb configure -command "$w.f.t yview" $w.f.t tag configure h1 -font {{} 18 bold} -justify center -foreground red $w.f.t tag configure h2 -font {{} 14 italic} -justify center $w.f.t tag configure h3 -font {{} 10 bold } $w.f.t insert end "$Prog(Title)\n" h1 $w.f.t insert end "Version $Prog(Version)\n\n" h2 $w.f.t insert end "$Prog(Title) is a GUI-demo that acts as a simple invoice-program.\nThere is a form for data-enty, and output of that data to screen and printer.\n\n" $w.f.t insert end "HOTKEYS:\n" h3 $w.f.t insert end "* F1 - Show this help.\n* F2 - Activate Debug/Console.\n* F4 - Exit program.\n" $w.f.t insert end "* F5 - Edit: Clear entry-fields.\n* F6, F7 - Edit: Fill entry-fields with testdata.\n" $w.f.t insert end "* F9 - Output data to screen.\n\n" $w.f.t insert end "DEBUG:\n" h3 $w.f.t insert end " The console offers access to the program's internals and variables.\n\n" $w.f.t insert end "Commands:\n ?: show console-help, e: Exit program.\n" $w.f.t insert end " d0: Clear entry-fields. d1, d2: Fill-in testdata.\n" $w.f.t insert end " s0, s1, s2, s3: Show data in different formats.\n\n" $w.f.t insert end "You can enter any tcl-command in the console, for example:\n" $w.f.t insert end "% info proc\n" $w.f.t insert end "% info var\n" $w.f.t insert end "% puts \$maxFNr\n" $w.f.t insert end "% parray data\n" $w.f.t insert end "% set data(3) \"Johnson\"\n" $w.f.t insert end "\n" $w.f.t insert end "CREDITS:\n" h3 $w.f.t insert end "Written by $Prog(Author) ($Prog(Contact)), source at: $Prog(HomePage).\n" $w.f.t configure -state disabled pack $w.f -side top -expand 1 -fill both pack $w.f.t -side left -expand 1 -fill both pack $w.f.sb -side right -fill y frame $w.f2 pack $w.f2 -side top -pady 8 button $w.f2.b -text "Close" -command "wm withdraw $w" -width 9 pack $w.f2.b wm protocol $w WM_DELETE_WINDOW "wm withdraw $w" focus $w.f.t };# Help #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ # Routines to edit/fill in data: proc data0 {} { #: Clear data in entry-fields for {set f 1} {$f <= $::maxFNr} {incr f} { set ::data($f) "" } #set ::data(1) "2015-05-20" set cs [clock seconds] set td [clock format $cs -format "%Y-%m-%d"] set ::data(1) "$td" focus .f1.enDate } proc data1 {} { #: Fill entry-fields with testdata set ::data(1) "2015-05-13" set ::data(2) "Mr." set ::data(3) "Smith" set ::data(4) "Mainstreet 123" set ::data(5) "Newtown" set ::data(6) "555-6789" set ::data(7) "0815" set ::data(8) "T-Shirt ultrablack, size XL" set ::data(9) " 9.99" set ::data(10) "2" set ::data(11) "19.98" focus $::startF1 } proc data2 {} { #: Fill entry-fields with testdata set fNr 0 set values { "2015-05-14" "Mrs." "Miller" "Highstreet 99" "Oldsville" "555-9876" "0816" "T-Shirt XL" "12.34" "1" "12.34" } foreach v $values { incr fNr set ::data($fNr) $v } focus $::startF2 } #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ # Output-Routines: proc showData0 {} { puts "INVOICE-data:" parray ::data } proc showData1 {} { #: Show the data on the console global data maxFNr puts "INVOICE\n==" for {set f 1} {$f <= $maxFNr} {incr f} { puts "Field#$f : $data($f)" } puts "--\nTHANKS !" } proc showData2 {} { #: Show the data in a simple messagebox global data maxFNr set txt "INVOICE\n==\n" for {set f 1} {$f <= $maxFNr} {incr f} { append txt "Field#$f : $data($f)\n" } append txt "--\nTHANKS !" tk_messageBox -icon info -message $txt -title "ShowData2" } proc showData3 {} { ;# from: TkOverload Help #: Show the data formatted like an invoice-letter global data catch {destroy .dia} toplevel .dia wm title .dia "Invoice" wm geom .dia "+[expr {[winfo x .] + [winfo width .] + 10}]+[winfo y .]" text .dia.t -relief raised -wrap word -width 70 -height 23 \ -padx 10 -pady 10 -cursor {} -yscrollcommand {.dia.sb set} scrollbar .dia.sb -orient vertical -command {.dia.t yview} button .dia.dismiss -text Dismiss -command {destroy .dia} pack .dia.dismiss -side bottom -pady 10 pack .dia.sb -side right -fill y pack .dia.t -side top -expand 1 -fill both set bold "[font actual [.dia.t cget -font]] -weight bold" set italic "[font actual [.dia.t cget -font]] -slant italic" .dia.t tag config h1 -justify center -foregr blue -font "Times 20 bold" .dia.t tag configure h2 -justify left -font "Times 12 bold" .dia.t tag configure bold -font $bold .dia.t tag configure ital -font $italic .dia.t tag configure n -lmargin1 15 -lmargin2 15 .dia.t insert end "Invoice\n" h1 # Todo: place picture with logo here set m "--- --- --- --- --- ---\n\n" .dia.t insert end "From:\n" h2 .dia.t insert end "$data(Adr1)\n$data(Adr2)\n$data(Adr3)\n$data(Adr4)\n" bold $m .\n\n .dia.t insert end "To:\n" h2 .dia.t insert end "$data(2) $data(3)\n$data(4)\n$data(5)\n" bold $m .\n\n # Todo: tweak formatting #.dia.t insert end "Item Description Unit Price Quantity Amount\n" bold $m n foreach field $::FieldNames2 { .dia.t insert end "$field " bold } .dia.t insert end "\n" #set m "$data(8) x $data(7) $data(9) -- $ $data(10) ea. = $ $data(11)\n" set m "$data(7) $data(8) $data(9) * $data(10) = $ $data(11)\n" .dia.t insert end $m n set m "$ $data(11)\n\n" .dia.t insert end "Total:\n" bold $m n set m "" append m "This product is meant for educational purposes only. " append m "Keep away from fire or flames. " append m "For recreational use only. " append m "Some assembly required. " append m "Do not use while operating a motor vehicle or heavy equipment. " append m "Any resemblance to real persons, living or dead is purely coincidental. " append m "Avoid contact with skin. " append m "No other warranty expressed or implied. " append m "One size fits all. " append m "Do not disturb. " append m "Action figures sold separately. " append m "Batteries not included. " append m "Price does not include taxes. " append m "Use only as directed. " append m "Keep away from sunlight. " append m "All rights reserved. " append m "Keep cool. \n" .dia.t insert end "Standard Disclaimer\n" bold $m n .dia.t config -state disabled focus .dia.t } # Todo: proc printData {} { Dummy printData } #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ #: Test+Debug: proc ? {} { help } proc help {} { puts "\n$::Prog(Title) $::Prog(Version)" puts "--" puts "argv0 : $::argv0" puts "argv : $::argv" puts "tcl/tk: $::tcl_patchLevel / $::tk_patchLevel \n" puts "d0,d1,d2, s0..s3:data0,data1,data2, showData0..3 ?:help e:exit \n" } proc d0 {} { data0 } proc d1 {} { data1 } proc d2 {} { data2 } proc s0 {} { showData0 } proc s1 {} { showData1 } proc s2 {} { showData2 } proc s3 {} { showData3 } proc e {} { exit } #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+ #: Main: wm title . "$Prog(Title) $Prog(Version)" help # Todo: make and include an icon here if {[file exists demo.ico]} { wm iconbitmap . demo.ico } focus -force . ### EOF ###
Example icon: InvoiceDemo_icon
It looks like the upload-procedure of the wiki doesn't like .ico - files.
So, just copy any icon you like to the location of the program, and rename it to demo.ico
Dump of the contents of the entry-fields, filled with testdata:
INVOICE == Field#1 : 2015-05-13 Field#2 : Mr. Field#3 : Smith Field#4 : Mainstreet 123 Field#5 : Newtown Field#6 : 555-6789 Field#7 : 0815 Field#8 : T-Shirt ultrablack, size XL Field#9 : 9.99 Field#10 : 2 Field#11 : 19.98
MiHa: This is a nice small demo of a simple program using only basic, essential GUI-features:
There is lots of more advanced GUI-stuff not covered so far, e.g.:
But if any of that gets included, and the program gets more complex because of that,
it might be better to put that into a separate program Invoice-Demo2.
The data is just simple strings of text, all of the same length, no validation, no calculation, no special logic, etc.
This definitely needs work for any real-world application.
BTW, I haven't found yet any good, nice, short routine for data-entry (such as m+ for menus).
Well, I tried to do a bit here:
The size of the grid-columns and the alignment of the labels in these separated sections don't match any more,
To keep the layout looking good requires tweaking.
That means the code there quickly gets complicated and hard-to-understand.
This is not good for demo that should be easy-to-understand.
Also, there is no file-I/O for data or configuration.
I haven't looked into the printing stuff yet, so if anybody thinks he is up to the task, you are welcome to fill in that part.
All in all, it looks like printing from Tcl/tk in a "native, portable way" is kind of a hard problem, and still mostly unsolved.