GUI component interaction: Difference between revisions

From Rosetta Code
Content added Content deleted
({{omit from|PARI/GP}})
(C# version)
Line 104: Line 104:
return app.exec( ) ;
return app.exec( ) ;
}</lang>
}</lang>

=={{header|C_sharp|C#}}==
C# 3.0 with Windows Forms; compile as csc -t:winexe Program.cs on MS.NET or as gmcs -t:winexe Program.cs on Mono.
<lang c_sharp>using System;
using System.ComponentModel;
using System.Windows.Forms;

class RosettaInteractionForm : Form
{
// Model used for DataBinding.
// Notifies bound controls about Value changes.
class NumberModel: INotifyPropertyChanged
{

Random rnd = new Random();

// initialize event with empty delegate to avoid checks on null
public event PropertyChangedEventHandler PropertyChanged = delegate {};

int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
// Notify bound control about value change
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}

public void ResetToRandom(){
Value = rnd.Next(5000);
}
}

NumberModel model = new NumberModel{ Value = 0};
RosettaInteractionForm()
{
//MaskedTextBox is a TextBox variety with built-in input validation
var tbNumber = new MaskedTextBox
{
Mask="0000", // allow 4 decimal digits only
ResetOnSpace = false, // don't enter spaces
Dock = DockStyle.Top // place at the top of form
};
// bound TextBox.Text to NumberModel.Value;
tbNumber.DataBindings.Add("Text", model, "Value");

var btIncrement = new Button{Text = "Increment", Dock = DockStyle.Bottom};
btIncrement.Click += delegate
{
model.Value++;
};
var btDecrement = new Button{Text = "Decrement", Dock = DockStyle.Bottom};
btDecrement.Click += delegate
{
model.Value--;
};
var btRandom = new Button{ Text="Reset to Random", Dock = DockStyle.Bottom };
btRandom.Click += delegate
{
model.ResetToRandom();
};
Controls.Add(tbNumber);
Controls.Add(btIncrement);
Controls.Add(btDecrement);
Controls.Add(btRandom);
}
static void Main()
{
Application.Run(new RosettaInteractionForm());
}
}
</lang>


=={{header|J}}==
=={{header|J}}==

Revision as of 23:38, 30 December 2010

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

C++

with library Qt 4.4 , using (under Linux) first qmake -project and then qmake -o Makefile <projectfile>, then make

file interaction.h

<lang c++>#ifndef INTERACTION_H

  1. define INTERACTION_H
  2. include <QWidget>

class QPushButton ; class QLineEdit ; class QVBoxLayout ; class MyWidget : public QWidget {

  Q_OBJECT 

public :

  MyWidget( QWidget *parent = 0 ) ;

private :

  QLineEdit *entryField ;
  QPushButton *increaseButton ;
  QPushButton *randomButton ;
  QVBoxLayout *myLayout ;

private slots :

  void doIncrement( ) ;
  void findRandomNumber( ) ;

} ;

  1. endif</lang>
file interaction.cpp

<lang c++>#include <QPushButton>

  1. include <QLineEdit>
  2. include <QMessageBox>
  3. include <QString>
  4. include <QRegExpValidator>
  5. include <QVBoxLayout>
  6. include <QRegExp>
  7. include <ctime> //for the srand initialization
  8. include <cstdlib> //for the random number
  9. include "interaction.h"

MyWidget::MyWidget (QWidget *parent ) : QWidget( parent ) {

  myLayout = new QVBoxLayout( ) ;
  entryField = new QLineEdit( "0" ) ;
  QRegExp rx( "\\d+" ) ;
  QValidator *myvalidator = new QRegExpValidator( rx , this ) ;
  entryField->setValidator( myvalidator ) ;
  increaseButton = new QPushButton( "increase" ) ;
  connect( increaseButton, SIGNAL( clicked( ) ) ,

this , SLOT( doIncrement( ) )) ;

  randomButton = new QPushButton( "random" ) ;
  connect( randomButton , SIGNAL( clicked( ) ) ,

this , SLOT ( findRandomNumber( ) )) ;

  myLayout->addWidget( entryField ) ;
  myLayout->addWidget( increaseButton ) ;
  myLayout->addWidget( randomButton ) ;
  setLayout( myLayout ) ;

}

void MyWidget::doIncrement( ) {

  bool ok ;
  int zahl = entryField->text( ).toInt( &ok, 10 ) ;
  entryField->setText( QString( "%1").arg( ++zahl ) ) ;

}

void MyWidget::findRandomNumber( ) {

  QMessageBox msgBox( this ) ;
  msgBox.setText( "Do you want to create a random number ?" ) ;
  msgBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No ) ;
  int ret = msgBox.exec( ) ;
  switch ( ret ) {
     case QMessageBox::Yes :

srand( time( 0 ) ) ; int zahl = random( ) ; entryField->setText( QString( "%1" ).arg( zahl )) ; break ;

  }

}</lang>

file main.cpp 

<lang c++>#include <QApplication>

  1. include "interaction.h"

int main( int argc , char *argv[ ] ) {

  QApplication app( argc, argv ) ;
  MyWidget theWidget ;
  theWidget.show( ) ;
  return app.exec( ) ;

}</lang>

C_sharp

C# 3.0 with Windows Forms; compile as csc -t:winexe Program.cs on MS.NET or as gmcs -t:winexe Program.cs on Mono. <lang c_sharp>using System; using System.ComponentModel; using System.Windows.Forms;

class RosettaInteractionForm : Form {

   // Model used for DataBinding.
   // Notifies bound controls about Value changes.
   class NumberModel: INotifyPropertyChanged
   {
       Random rnd = new Random();
       // initialize event with empty delegate to avoid checks on null
       public event PropertyChangedEventHandler PropertyChanged = delegate {};
       int _value;
       public int Value
       {
           get { return _value; }
           set 
           { 
               _value = value;
               // Notify bound control about value change
               PropertyChanged(this, new PropertyChangedEventArgs("Value"));
           }
       }
       public void ResetToRandom(){
           Value = rnd.Next(5000);
       }
   }
   NumberModel model = new NumberModel{ Value = 0};
   
   RosettaInteractionForm()    
   {
       //MaskedTextBox is a TextBox variety with built-in input validation
       var tbNumber = new MaskedTextBox
                       { 
                           Mask="0000",            // allow 4 decimal digits only
                           ResetOnSpace = false,   // don't enter spaces
                           Dock = DockStyle.Top    // place at the top of form
                       };
       // bound TextBox.Text to NumberModel.Value;
       tbNumber.DataBindings.Add("Text", model, "Value");
       var btIncrement = new Button{Text = "Increment", Dock = DockStyle.Bottom};
       btIncrement.Click += delegate
                       {
                           model.Value++;
                       };
       var btDecrement = new Button{Text = "Decrement", Dock = DockStyle.Bottom};
       btDecrement.Click += delegate
                       {
                           model.Value--;
                       };
       var btRandom = new Button{ Text="Reset to Random", Dock = DockStyle.Bottom };
       btRandom.Click += delegate
                       {
                           model.ResetToRandom();
                       };
       Controls.Add(tbNumber);
       Controls.Add(btIncrement);
       Controls.Add(btDecrement);
       Controls.Add(btRandom);
   }
   static void Main()
   {
       Application.Run(new RosettaInteractionForm());
   }

} </lang>

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. 0 = 2 wdquery 'Confirm';'Reset to random number?' 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 j>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>

Ruby

Uses the Shoes library. <lang ruby>Shoes.app do

 stack do
   @textbox = edit_line
   @textbox.change do
     @textbox.text = @textbox.text.gsub(/[^\d]/, ) and alert "Input must be a number!" if @textbox.text !~ /^\d*$/
   end
   flow do
     button "Increment" do
       @textbox.text = @textbox.text.to_i + 1
     end
     button "Random" do
       @textbox.text = rand 5000 if confirm "Do you want a random number?"
     end
   end
 end

end</lang>

Tcl

Library: Tk

<lang tcl>package require Tk

      1. --- Our data Model! ---###
  1. A single variable will do just fine

set field 0

      1. --- Lay out the GUI components in our View ---###
  1. We use the Ttk widget set here; it looks much better on Windows and OSX
  1. First, a quick hack to make things look even nicer

place [ttk::frame .bg] -relwidth 1 -relheight 1

  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}]

  1. Now, a pair of buttons

pack [ttk::button .inc -text "increment" -command step] pack [ttk::button .rnd -text "random" -command random]

      1. --- Now we define the behaviors, the Controller ---###
  1. How to respond to a click on the "increment" button

proc step {} {

   global field
   incr field

}

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