GUI component interaction
You are encouraged to solve this task according to the task description, using any language you may know.
Almost every application needs to communicate with the user in some way. Therefore, a substantial part of the code deals with the interaction of program logic with GUI components. Typically, the following is needed:
- put values into input fields under program control
- read and check input from the user
- pop up dialogs to query the user for further information
The task: For a minimal "application", write a program that presents a form with three components to the user: A numeric input field ("Value") and two buttons ("increment" and "random").
The field is initialized to zero. The user may manually enter a new value into the field, or increment its value with the "increment" button. Entering a non-numeric value should be either impossible, or issue an error message.
Pressing the "random" button presents a confirmation dialog, and resets the field's value to a random value if the answer is "Yes".
(This task may be regarded as an extension of the task Simple windowed application).
J
<lang j>INTERACT=: 0 : 0 pc interact closeok; xywh 6 6 48 12;cc Value edit; xywh 6 18 48 12;cc increment button;cn "+"; xywh 6 30 48 12;cc random button;cn "?"; pas 6 6;pcenter; rem form end; )
interact_run=: 3 : 0
wd INTERACT wd 'set Value 0;' wd 'pshow;'
)
interact_close=: 3 : 0
wd'pclose'
)
interact_Value_button=: 3 : 0
wd 'set Value ',":{.0". Value
)
interact_increment_button=: 3 : 0
wd 'set Value ',":1+{.0". Value
)
interact_random_button=: 3 : 0
if.'YES'-:wd 'mb Confirm Randomize? mb_yesno'do. wd 'set Value ',":?100 end.
)</lang>
Note: I used an edit box for the value, and edit boxes do not get onChange events, so rejection of non-numeric values will be delayed until the use of a button (or pressing enter).
Example use:
<lang>interact_run</lang>
Liberty BASIC
Input Verification
<lang lb>nomainwin
textbox #demo.val, 20, 50, 90, 24 button #demo.inc, "Increment", [btnIncrement], UL, 20, 90, 90, 24 button #demo.rnd, "Random", [btnRandom], UL, 20, 120, 90, 24 open "Rosetta Task: GUI component interaction" for window as #demo #demo "trapclose [quit]" validNum$ = "0123456789." #demo.val 0
wait
[quit]
close #demo
end
[btnIncrement]
#demo.val "!contents? nVal$" nVal$ = trim$(nVal$) if left$(nVal$, 1) = "-" then neg = 1 nVal$ = mid$(nVal$, 2) else neg = 0 end if validNum = 1 nDecs = 0 for i = 1 to len(nVal$) if instr(validNum$, mid$(nVal$, i, 1)) = 0 then validNum = 0 end if if mid$(nVal$, i, 1) = "." then nDecs = nDecs + 1 end if next i if nDecs > 1 then validNum = 0 end if if neg = 1 then nVal$ = "-";nVal$ end if if validNum = 0 then notice nVal$;" does not appear to be a valid number. " + _ "(Commas are not allowed.)" else nVal = val(nVal$) nVal = nVal + 1 end if #demo.val nVal
wait
[btnRandom]
confirm "Reset value to random number";yn$ if yn$ = "yes" then nVal = int(rnd(1) * 100) + 1 #demo.val nVal end if
wait</lang>
Impossible to type non-numeric characters
<lang lb>nomainwin
stylebits #demo.val, _ES_NUMBER, 0, 0, 0 textbox #demo.val, 20, 50, 90, 24 button #demo.inc, "Increment", [btnIncrement], UL, 20, 90, 90, 24 button #demo.rnd, "Random", [btnRandom], UL, 20, 120, 90, 24 open "Rosetta Task: GUI component interaction" for window as #demo #demo "trapclose [quit]" #demo.val 0
wait
[quit]
close #demo
end
[btnIncrement]
#demo.val "!contents? nVal" nVal = nVal + 1 #demo.val nVal
wait
[btnRandom]
confirm "Reset value to random number";yn$ if yn$ = "yes" then nVal = int(rnd(1) * 100) + 1 #demo.val nVal end if
wait</lang>
Oz
Using Mozart's standard GUI library, building a small desktop application: <lang oz>declare
[QTk] = {Module.link ['x-oz://system/wp/QTk.ozf']}
proc {Main} MaxValue = 1000 NumberWidget GUI = lr( numberentry(init:1 min:0 max:MaxValue handle:NumberWidget) button(text:"Increase" action:proc {$} OldVal = {NumberWidget get($)} in {NumberWidget set(OldVal+1)} end) button(text:"Random" action:proc {$} if {Ask "Reset to random?"} then Rnd = {OS.rand} * MaxValue div {OS.randLimits _} in {NumberWidget set(Rnd)} end end) ) Window = {QTk.build GUI} in {Window show} end
fun {Ask Msg} Result Box = {QTk.build td(message(init:Msg) lr(button(text:"Yes" action:proc {$} Result=true {Box close} end) button(text:"No" action:proc {$} Result=false {Box close} end) ))} in {Box show} {Box wait} Result end
in
{Main}</lang>
As a web application, using the "Roads" web programming library. Connect your browser to http://localhost:8080/start after starting the program. <lang oz>declare
[Roads] = {Module.link ['x-ozlib://wmeyer/roads/Roads.ozf']}
MaxValue = 1000
fun {Start Session} {Page 0} end
fun {Page Val} html( body( %% numerical input with an HTML form local NewVal in form( {NumberInput Val NewVal} input(type:submit) method:post action:fun {$ _} {Page NewVal} end ) end %% link with button functionality a("Increase" href:fun {$ _} {Page Val+1} end) " " %% another "button-link" a("Random" href:fun {$ S} p("Reset to random? - " a("Yes" href:fun {$ _} Rnd = {OS.rand} * MaxValue div {OS.randLimits _} in {Page Rnd} end) " " a("No" href:fun {$ _} {Page Val} end) ) end) )) end
%% a "formlet", managing input of an integer value fun {NumberInput OldVal NewVal} input(type:text validate:int_in(0 MaxValue) value:{Int.toString OldVal} bind:proc {$ Str} NewVal = {String.toInt Str.escaped} end ) end
in
{Roads.registerFunction 'start' Start} {Roads.run}</lang>
PicoLisp
The standard PicoLisp GUI is HTTP based. Connect your browser to http://localhost:8080 after starting the following script. <lang PicoLisp>#!/usr/bin/picolisp /usr/lib/picolisp/lib.l
(load "@ext.l" "@lib/http.l" "@lib/xhtml.l" "@lib/form.l")
(de start ()
(and (app) (zero *Number)) (action (html 0 "Increment" "lib.css" NIL (form NIL (gui '(+Var +NumField) '*Number 20 "Value") (gui '(+JS +Button) "increment" '(inc '*Number) ) (gui '(+Button) "random" '(ask "Reset to a random value?" (setq *Number (rand)) ) ) ) ) ) )
(server 8080 "@start") (wait)</lang>
PureBasic
<lang PureBasic>Enumeration
#StringGadget #Increment #Random
EndEnumeration
If OpenWindow(0,#PB_Ignore,#PB_Ignore,180,50,"PB-GUI",#PB_Window_SystemMenu)
StringGadget(#StringGadget,5,5,170,20,"",#PB_String_Numeric) ButtonGadget(#Increment,5,25,80,20, "Increment") ButtonGadget(#Random, 90,25,80,20, "Random") Repeat Event=WaitWindowEvent() If Event=#PB_Event_Gadget Select EventGadget() Case #Increment CurrentVal=Val(GetGadgetText(#StringGadget)) SetGadgetText(#StringGadget,Str(CurrentVal+1)) Case #Random Flag=#PB_MessageRequester_YesNo Answer=MessageRequester("Randomize","Are you sure?",Flag) If Answer=#PB_MessageRequester_Yes SetGadgetText(#StringGadget,Str(Random(#MAXLONG))) EndIf EndSelect EndIf Until Event=#PB_Event_CloseWindow CloseWindow(0)
EndIf</lang>
Tcl
<lang tcl>package require Tk
- --- Our data Model! ---###
- A single variable will do just fine
set field 0
- --- Lay out the GUI components in our View ---###
- We use the Ttk widget set here; it looks much better on Windows and OSX
- First, a quick hack to make things look even nicer
place [ttk::frame .bg] -relwidth 1 -relheight 1
- A labelled frame containing an entry field constrained to use numbers
pack [ttk::labelframe .val -text "Value"] pack [ttk::entry .val.ue -textvariable field \ -validate key -invalidcommand bell \ -validatecommand {string is integer %P}]
- Now, a pair of buttons
pack [ttk::button .inc -text "increment" -command step] pack [ttk::button .rnd -text "random" -command random]
- --- Now we define the behaviors, the Controller ---###
- How to respond to a click on the "increment" button
proc step {} {
global field incr field
}
- How to respond to a click on the "random" button
proc random {} {
global field if {[tk_messageBox -type yesno -parent . \
-message "Reset to random?"] eq "yes"} { set field [expr {int(rand() * 5000)}]
}
}</lang>