Version 30 of Invoice-Demo

Updated 2015-05-22 19:45:45 by MiHa

if 0 {


Introduction

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:

}


Program 1

 # InvoiceDemo07.tcl - Mike Hase - 2015-05-19
 # http://wiki.tcl.tk/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.07"
  set Prog(Date)     "2015-05-20"
  set Prog(Author)   "Mike Hase"
  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

 set FieldNames1 {"Date                " Title Name Street City Phone}
#set FieldNames2 { art1Nr art1qty art1desc art1price price1}
 set FieldNames2 { Article-Number Quantity Description Price Total}

 set fNr   0
 set artNr 0

 # Build data-entry form:
 ## Todo: put this form into its own dialog

 #: Frame for customer-data:
 frame .f1   ;# -background red
 
 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 -text $field]
        set en [entry .f1.en$field -justify left -textvar data($fNr) ]
        grid $lb $en -padx 4 -pady 2
        grid $lb -sticky e
        grid $en -sticky ew
 }

 #: 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 -text $field]
        set en [entry .f2.en$field -justify right -textvar data($fNr) ]
        grid $lb $en -padx 4 -pady 2
        grid $lb -sticky e
        grid $en -sticky ew
 }
 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 { showData  }
 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 http://wiki.tcl.tk/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      showData  
  m+ &Output &Print     printData 

  m+ &Help &About       About
  m+ &Help &Help        Help
  m+ &Help -----
  m+ &Help &Debug     { console show }


  bind all <Key-F1>     About
  bind all <Key-F2>   { console show }
  bind all <Key-F4>     exit 

  bind all <Key-F5>     data0 
  bind all <Key-F6>     data1 
  bind all <Key-F7>     data2 
  bind all <Key-F8>     showData  

 #---+----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

 #---+----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) ""
   }
 }

 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) "2"
   set ::data(9) "T-Shirt"
   set ::data(10) " 9.99"
   set ::data(11) "19.98"
 }

 proc data2 {} {
 #: Fill entry-fields with testdata
   set fNr   0
   set values {
      "2015-05-14"
      "Mrs." "Miller" "Highstreet 99" "Oldsville" "555-9876"
      "0816" "1" "T-Shirt XL" "12.34" "12.34"
   }
   foreach v $values {
        incr fNr
        set ::data($fNr) $v
   }
 }

 #---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+

 # Output-Routines:

  proc listData {} { 
  #: 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 showData  {} { 
  #: 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 "ShowData"
  }

 # 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,s,l:data0,data1,data2,listData,showData  ?:help  e:exit \n"
  }

  proc d0 {} { data0 }
  proc d1 {} { data1 }
  proc d2 {} { data2 }
  proc l  {} { listData }
  proc s  {} { showData }

  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


Output

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

Remarks

MiHa: This is a nice small demo of a simple program using only basic, essential GUI-features:

  • Menu
  • Buttons (to see them, change that "if 0"-section to "if 1")
  • Hotkeys (F1..F8, etc.)
  • Labels
  • Entry-fields
  • Messagebox
  • Title at top of window
  • Icon at top of window (currently, this needs an external file)
  • frames, pack and grid to manage the layout

There is lots of more advanced GUI-stuff not covered so far, e.g.:

  • Checkboxes
  • Radiobuttons
  • Fancy textformating, fonts (i.e. for a nice help-window)
  • Listbox / Multicolumn listbox / Tables
  • Scrollbars
  • dialog-windows separate from the main-window (i.e. for the data-entry form)

But if any of that gets included, and the program gets more complex,
it might be better to put that into a separate program.

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).

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.


See also

  • GUI Primer - essentially my notes researching GUI-stuff on tcl/tk

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.