A First (Real) Example
With that out of the way, let's try a slightly more useful example, which will give you an initial feel for what the code behind a Tk program looks like.
Design
The example we'll use is a simple GUI tool that will convert a number of feet to the equivalent number of meters. If we were to sketch this out, it might look something like this:
A sketch of our feet to meters conversion program.
So it looks like we have a short text entry widget that will let us type in the number of feet, and a 'Calculate' button that will get the value out of that entry, perform the calculation, and then put the resulting number of meters on the screen just below where the entry is. We've also got three static labels ("feet", "is equivalent to", and "meters") which help our user figure out how to use the interface.
In terms of layout, things seem to naturally divide into three columns and three rows:
The layout of our user interface, which follows a 3 x 3 grid.
Code
Now here is the Tcl/Tk code to create this program.
package require Tk wm title . "Feet to Meters" grid [ttk::frame .c -padding "3 3 12 12"] -column 0 -row 0 -sticky nwes grid columnconfigure . 0 -weight 1; grid rowconfigure . 0 -weight 1 grid [ttk::entry .c.feet -width 7 -textvariable feet] -column 2 -row 1 -sticky we grid [ttk::label .c.meters -textvariable meters] -column 2 -row 2 -sticky we grid [ttk::button .c.calc -text "Calculate" -command calculate] -column 3 -row 3 -sticky w grid [ttk::label .c.flbl -text "feet"] -column 3 -row 1 -sticky w grid [ttk::label .c.islbl -text "is equivalent to"] -column 1 -row 2 -sticky e grid [ttk::label .c.mlbl -text "meters"] -column 3 -row 2 -sticky w foreach w [winfo children .c] {grid configure $w -padx 5 -pady 5} focus .c.feet bind . <Return> {calculate} proc calculate {} { if {[catch { set ::meters [expr {round($::feet*0.3048*10000.0)/10000.0}] }]!=0} { set ::meters "" } }
Now here is the RubyTk code to create this program.
require 'tk' require 'tkextlib/tile' root = TkRoot.new {title "Feet to Meters"} content = Tk::Tile::Frame.new(root) {padding "3 3 12 12"}.grid( :sticky => 'nsew') TkGrid.columnconfigure root, 0, :weight => 1; TkGrid.rowconfigure root, 0, :weight => 1 $feet = TkVariable.new; $meters = TkVariable.new f = Tk::Tile::Entry.new(content) {width 7; textvariable $feet}.grid( :column => 2, :row => 1, :sticky => 'we' ) Tk::Tile::Label.new(content) {textvariable $meters}.grid( :column => 2, :row => 2, :sticky => 'we'); Tk::Tile::Button.new(content) {text 'Calculate'; command {calculate}}.grid( :column => 3, :row => 3, :sticky => 'w') Tk::Tile::Label.new(content) {text 'feet'}.grid( :column => 3, :row => 1, :sticky => 'w') Tk::Tile::Label.new(content) {text 'is equivalent to'}.grid( :column => 1, :row => 2, :sticky => 'e') Tk::Tile::Label.new(content) {text 'meters'}.grid( :column => 3, :row => 2, :sticky => 'w') TkWinfo.children(content).each {|w| TkGrid.configure w, :padx => 5, :pady => 5} f.focus root.bind("Return") {calculate} def calculate begin $meters.value = (0.3048*$feet*10000.0).round()/10000.0 rescue $meters.value = '' end end Tk.mainloop
Now here is the Perl code to create this program.
use Tkx; Tkx::wm_title(".", "Feet to Meters"); Tkx::ttk__frame(".c", -padding => "3 3 12 12"); Tkx::grid( ".c", -column => 0, -row => 0, -sticky => "nwes"); Tkx::grid_columnconfigure( ".", 0, -weight => 1); Tkx::grid_rowconfigure(".", 0, -weight => 1); Tkx::ttk__entry(".c.feet", -width => 7, -textvariable => \$feet); Tkx::grid(".c.feet", -column => 2, -row => 1, -sticky => "we"); Tkx::ttk__label(".c.meters", -textvariable => \$meters); Tkx::grid(".c.meters", -column => 2, -row => 2, -sticky => "we"); Tkx::ttk__button(".c.calc", -text => "Calculate", -command => sub {calculate();}); Tkx::grid(".c.calc", -column => 3, -row => 3, -sticky => "w"); Tkx::grid( Tkx::ttk__label(".c.flbl", -text => "feet"), -column => 3, -row => 1, -sticky => "w"); Tkx::grid( Tkx::ttk__label(".c.islbl", -text => "is equivalent to"), -column => 1, -row => 2, -sticky => "e"); Tkx::grid( Tkx::ttk__label(".c.mlbl", -text => "meters"), -column => 3, -row => 2, -sticky => "w"); foreach (Tkx::SplitList(Tkx::winfo_children(".c"))) { Tkx::grid_configure($_, -padx => 5, -pady => 5); } Tkx::focus(".c.feet"); Tkx::bind(".", "<Return>", sub {calculate();}); sub calculate { $meters = int(0.3048*$feet*10000.0+.5)/10000.0 || ''; } Tkx::MainLoop();
As we'll see in the next chapter, there's another, more object-oriented way to do exactly the same thing. Are we surprised?
Now here is the Python code to create this program.
from tkinter import * from tkinter import ttk def calculate(*args): try: value = float(feet.get()) meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0) except ValueError: pass root = Tk() root.title("Feet to Meters") mainframe = ttk.Frame(root, padding="3 3 12 12") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) mainframe.columnconfigure(0, weight=1) mainframe.rowconfigure(0, weight=1) feet = StringVar() meters = StringVar() feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet) feet_entry.grid(column=2, row=1, sticky=(W, E)) ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E)) ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=3, row=3, sticky=W) ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W) ttk.Label(mainframe, text="is equivalent to").grid(column=1, row=2, sticky=E) ttk.Label(mainframe, text="meters").grid(column=3, row=2, sticky=W) for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) feet_entry.focus() root.bind('<Return>', calculate) root.mainloop()
And the resulting user interface:
Screenshot of our completed feet to meters user interface (on Mac OS X, Windows and Linux).
沒有留言:
張貼留言