GUI enabling/disabling of controls

From Rosetta Code
Revision as of 19:40, 19 January 2011 by Eriksiers (talk | contribs) (added VB)
Task
GUI enabling/disabling of controls
You are encouraged to solve this task according to the task description, using any language you may know.

In addition to fundamental GUI component interaction, an application should dynamically enable and disable GUI components, to give some guidance to the user, and prohibit (inter)actions which are inappropriate in the current state of the application.

The task: Similar to the task GUI component interaction write a program that presents a form with three components to the user: A numeric input field ("Value") and two buttons ("increment" and "decrement").

The field is initialized to zero. The user may manually enter a new value into the field, increment its value with the "increment" button, or decrement the value with the "decrement" button.

The input field should be enabled only when its value is zero. The "increment" button only as long as the field's value is less then 10: When the value 10 is reached, the button should go into a disabled state. Analogously, the "decrement" button should be enabled only as long as the value is greater than zero.

Effectively, the user can now either increment up to 10, or down to zero. Manually entering values outside that range is still legal, but the buttons should reflect that and enable/disable accordingly.

C++

with Qt 4.4, creating project file with qmake -project, Makefile with qmake -o Makefile <projectfile> and finally make

file task.h

<lang cpp>#ifndef TASK_H

  1. define TASK_H
  1. include <QWidget>

class QPushButton ; class QString ; class QLineEdit ; class QLabel ; class QVBoxLayout ; class QHBoxLayout ;

class MyWidget : public QWidget {

   Q_OBJECT

public:

  MyWidget( QWidget *parent = 0 ) ;

private slots:

  void buttonChange( const QString & ) ;
  void addField( ) ;
  void subtractField( ) ;

private :

  QVBoxLayout *thisWidgetLayout ;
  QLabel *instruction ;
  QPushButton *increment ;
  QPushButton *decrement ;
  QLineEdit *entryField ;
  QHBoxLayout *lowerPart ;

} ;

  1. endif

</lang>

file task.cpp

<lang cpp>#include <QtGui>

  1. include <QString>
  2. include "task.h"

MyWidget::MyWidget ( QWidget *parent )

  : QWidget( parent ) {
  thisWidgetLayout = new QVBoxLayout ( this )  ;
  instruction = new QLabel ;
  instruction->setText( "Enter a number between 1 and 10 ! Numbers above 10 are decremented, below 0 incremented!" ) ;
  instruction->setWordWrap( true ) ;
  lowerPart = new QHBoxLayout ;
  entryField = new QLineEdit( "0" ) ;
  increment = new QPushButton( "Increment" ) ;
  decrement = new QPushButton( "Decrement" ) ;
  increment->setDefault( true ) ;
  connect( entryField , SIGNAL ( textChanged ( const QString &  ) ) , 

this , SLOT ( buttonChange( const QString & )) ) ;

  connect( entryField , SIGNAL ( textEdited ( const QString &  ) ) , 

this , SLOT ( buttonChange( const QString & )) ) ;

  connect( increment , SIGNAL ( clicked( ) ) , this ,

SLOT ( addField( ) )) ;

  connect( decrement , SIGNAL ( clicked( ) ) , this ,

SLOT ( subtractField( ))) ;

  lowerPart->addWidget( entryField ) ;
  lowerPart->addWidget( increment ) ;
  lowerPart->addWidget( decrement ) ;
  thisWidgetLayout->addWidget( instruction ) ;
  thisWidgetLayout->addLayout( lowerPart ) ;
  setLayout( thisWidgetLayout ) ;

}

void MyWidget::buttonChange( const QString & text ) {

  bool ok ;
  increment->setEnabled( text.toInt( &ok, 10 ) < 10 ) ;
  increment->setDisabled( text.toInt( &ok, 10 ) > 9 ) ;
  decrement->setEnabled( text.toInt( &ok, 10 ) > 0 ) ;
  decrement->setDisabled( text.toInt( &ok, 10 ) <= 0 ) ;
  if ( ! ( text == "0" ) ) 
     entryField->setReadOnly( true ) ;

}

void MyWidget::addField( ) {

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

}

void MyWidget::subtractField( ) {

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

}</lang>

main.cpp

<lang cpp>#include <QApplication>

  1. include "task.h"

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

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

} </lang>

C_sharp

Using Windows Forms; compile with csc -t:winexe Program.cs (on MS.NET) or 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
   {
       // 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"));
           }
       }
   }
   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 enabledIfZero = new Binding("Enabled", model, "Value");
       EnableControlWhen(tbNumber, value => value == 0);
       var btIncrement = new Button{Text = "Increment", Dock = DockStyle.Bottom};
       btIncrement.Click += delegate
                       {
                           model.Value++;
                       };
       EnableControlWhen(btIncrement, value => value < 10);
       var btDecrement = new Button{Text = "Decrement", Dock = DockStyle.Bottom};
       btDecrement.Click += delegate
                       {
                           model.Value--;
                       };
       EnableControlWhen(btDecrement, value => value > 0);
       Controls.Add(tbNumber);
       Controls.Add(btIncrement);
       Controls.Add(btDecrement);
   }
   // common part of creating bindings for Enabled property
   void EnableControlWhen(Control ctrl, Func<int, bool> predicate)
   {
       // bind Control.Enabled to NumberModel.Value
       var enabledBinding = new Binding("Enabled", model, "Value");
       // Format event is called when model value should be converted to Control value.
       enabledBinding.Format += (sender, args) =>
           {
               // Enabled property is of bool type.
               if (args.DesiredType != typeof(bool)) return;
               // set resulting value by applying condition
               args.Value = predicate((int)args.Value);
           };
       // as a result, control will be enabled if predicate returns true
       ctrl.DataBindings.Add(enabledBinding);
   }
   static void Main()
   {
       Application.Run(new RosettaInteractionForm());
   }

} </lang>

J

<lang J>task_run=: wd bind (noun define)

 pc task nosize;
 xywh 6 30 48 12;cc decrement button;cn "-";
 xywh 6 18 48 12;cc increment button;cn "+";
 xywh 6  6 48 12;cc Value edit; set Value 0;
 pas 6 6;pcenter;
 pshow;

)

task_close=: wd bind 'pclose'

task_Value_button=: update=: verb define

 wd 'set Value ',":n=.{.0".Value
 wd 'setenable Value ',":n=0
 wd 'setenable increment ',":n<10
 wd 'setenable decrement ',":n>0

)

task_increment_button=:verb define

 update Value=:":1+0".Value

) task_decrement_button=:verb define

 update Value=:":_1+0".Value

)</lang>

Example use:

<lang> task_run</lang>

Liberty BASIC

<lang lb>nomainwin

   textbox #demo.val, 20, 50, 90, 24
   button #demo.dec, "Decrement", [btnDecrement], UL, 20, 90, 90, 24
   button #demo.inc, "Increment", [btnIncrement], UL, 20, 120, 90, 24
   statictext #demo.txt, "Positive or negative whole numbers only.", 20, 170, 240, 24
   open "Rosetta Task: GUI enabling/disabling of controls" for window as #demo
   #demo "trapclose [quit]"
   #demo.val 0
   #demo.dec "!disable"

wait

[quit]

   close #demo

end

[btnDecrement]

   validNum = validNum()
   if validNum = 0 then
       #demo.val "!contents? nVal$"
       notice nVal$;" does not appear to be a valid whole number."
   else
       #demo.val "!contents? nVal"
       if nVal > 0 then
           nVal = nVal - 1
       end if
   end if
   #demo.val nVal
   call disEnableControls nVal

wait

[btnIncrement]

   validNum = validNum()
   if validNum = 0 then
       #demo.val "!contents? nVal$"
       notice nVal$;" does not appear to be a valid whole number."
   else
       #demo.val "!contents? nVal"
       if nVal < 10 then
           nVal = nVal + 1
       end if
   end if
   #demo.val nVal
   call disEnableControls nVal

wait

Function validNum()

   validNum$ = "0123456789"
   #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
   for i = 1 to len(nVal$)
       if instr(validNum$, mid$(nVal$, i, 1)) = 0 then
           validNum = 0
       end if
   next i

End Function

Sub disEnableControls nVal

   if nVal > 9 then
       #demo.inc "!disable"
   else
       #demo.inc "!enable"
   end if
   if nVal < 1 then
       #demo.dec "!disable"
   else
       #demo.dec "!enable"
   end if
   if nVal = 0 then
       #demo.val "!enable"
   else
       #demo.val "!disable"
   end if

End Sub</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 "Enable/Disable" "lib.css" NIL
        (form NIL
           (gui '(+Var +Able +NumField) '*Number '(=0 *Number) 20 "Value")
           (gui '(+Able +JS +Button) '(> 10 *Number) "increment"
              '(inc '*Number) )
           (gui '(+Able +JS +Button) '(gt0 *Number) "decrement"
              '(dec '*Number) ) ) ) ) )

(server 8080 "@start") (wait)</lang>

PureBasic

<lang PureBasic>Enumeration

 #TextGadget
 #AddButton
 #SubButton

EndEnumeration

Procedure UpdateGadgets(Value,UpdateValue=0)

 Overmax=0: UnderMin=0
 If Value>=10
   Overmax=1
 ElseIf Value<=0
   UnderMin=1
 EndIf
 DisableGadget(#AddButton,Overmax)
 DisableGadget(#SubButton,UnderMin)
 If UpdateValue
   SetGadgetText(#TextGadget,Str(Value))
 EndIf

EndProcedure

If OpenWindow(0,#PB_Ignore,#PB_Ignore,110,70,"PB-GUI",#PB_Window_SystemMenu)

 StringGadget(#TextGadget,10,10,90,20,"")
 ButtonGadget(#AddButton,10,40,30,20,"+")
 ButtonGadget(#SubButton,70,40,30,20,"-")
 UpdateGadgets(Value,1)
 Repeat 
   Event=WaitWindowEvent()
   If Event=#PB_Event_Gadget
     Gadget=EventGadget()
     Select Gadget
       Case #AddButton
         Value+1
         UpdateGadgets(Value,1)
       Case #SubButton
         Value-1
         UpdateGadgets(Value,1)
       Default
         EType=EventType()
         If EType=#PB_EventType_Change
           Value=Val(GetGadgetText(#TextGadget))
           UpdateGadgets(Value)
         EndIf
     EndSelect
   EndIf
   Until Event=#PB_Event_CloseWindow

EndIf</lang>

Tcl

Library: Tk

<lang tcl>package require Tk

  1. Model

set field 0

  1. View

place [ttk::frame .bg] -relwidth 1 -relheight 1; # Hack to make things look nice pack [ttk::labelframe .val -text "Value"] pack [ttk::entry .val.ue -textvariable field \ -validate key -invalidcommand bell \ -validatecommand {string is integer %P}] pack [ttk::button .inc -text "increment" -command up] pack [ttk::button .dec -text "decrement" -command down]

  1. Controller

proc up {} {

   global field
   incr field

} proc down {} {

   global field
   incr field -1

}

  1. Attach this controller to the Model; easier than manual calling

trace add variable field write updateEnables proc updateEnables {args} {

   global field
   .inc state [expr {$field < 10 ? "!disabled" : "disabled"}]
   .dec state [expr {$field > 0 ? "!disabled" : "disabled"}]

} updateEnables; # Force initial state of buttons</lang>

Visual Basic

In VB, windows are usually created in the IDE. The generated code is hidden from the user unless viewed outside of VB. For the sake of this task, I have included that code.

(Also, this sort of task would typically be performed by a "spinner" or "up-down" control, not by buttons.)

<lang vb>VERSION 5.00 Begin VB.Form Form1

  Caption         =   "Form1"
  ClientHeight    =   2265
  ClientLeft      =   60
  ClientTop       =   600
  ClientWidth     =   2175
  LinkTopic       =   "Form1"
  ScaleHeight     =   2265
  ScaleWidth      =   2175
  StartUpPosition =   3  'Windows Default
  Begin VB.CommandButton cmdDec 
     Caption         =   "Decrement"
     Enabled         =   0   'False
     Height          =   495
     Left            =   120
     TabIndex        =   2
     Top             =   1680
     Width           =   1215
  End
  Begin VB.CommandButton cmdInc 
     Caption         =   "Increment"
     Height          =   495
     Left            =   120
     TabIndex        =   1
     Top             =   1080
     Width           =   1215
  End
  Begin VB.TextBox txtValue 
     Height          =   495
     Left            =   120
     TabIndex        =   0
     Text            =   "0"
     Top             =   240
     Width           =   1215
  End

End Attribute VB_Name = "Form1" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False '-----user-written code begins here; everything above this line is hidden in the GUI----- Private Sub cmdDec_Click()

   If Val(txtValue.Text) > 0 Then txtValue.Text = Val(txtValue.Text) - 1

End Sub

Private Sub cmdInc_Click()

   If Val(txtValue.Text) < 10 Then txtValue.Text = Val(txtValue.Text) + 1

End Sub

Private Sub txtValue_Change()

   Select Case Val(txtValue.Text)
       Case Is < 0
           txtValue.Enabled = False
           cmdInc.Enabled = True
           cmdDec.Enabled = False
       Case Is > 9
           txtValue.Enabled = False
           cmdInc.Enabled = False
           cmdDec.Enabled = True
       Case 0
           txtValue.Enabled = True
           cmdInc.Enabled = True
           cmdDec.Enabled = False
       Case Else
           txtValue.Enabled = False
           cmdInc.Enabled = True
           cmdDec.Enabled = True
   End Select

End Sub</lang>