GUI enabling/disabling of controls: Difference between revisions
({{omit from|PARI/GP}}) |
(added Java version) |
||
Line 341: | Line 341: | ||
updateEnables; # Force initial state of buttons</lang> |
updateEnables; # Force initial state of buttons</lang> |
||
=={{header|Java}}== |
|||
This task is normally accomplished with a design pattern called the mediator. |
|||
{{works with|Java Virtual Machine|1.6}} |
|||
<lang Java> |
|||
import java.awt.BorderLayout; |
|||
import java.awt.FlowLayout; |
|||
import java.awt.GridLayout; |
|||
import java.awt.event.ActionEvent; |
|||
import java.awt.event.ActionListener; |
|||
import javax.swing.BorderFactory; |
|||
import javax.swing.JButton; |
|||
import javax.swing.JFrame; |
|||
import javax.swing.JLabel; |
|||
import javax.swing.JMenu; |
|||
import javax.swing.JMenuBar; |
|||
import javax.swing.JMenuItem; |
|||
import javax.swing.JOptionPane; |
|||
import javax.swing.JPanel; |
|||
import javax.swing.JScrollPane; |
|||
import javax.swing.JTextArea; |
|||
import javax.swing.JTextField; |
|||
import javax.swing.event.DocumentEvent; |
|||
import javax.swing.event.DocumentListener; |
|||
/** |
|||
* This frame allows a user to draft an email. |
|||
* @author Ranman |
|||
* @version Dec 30, 2010 |
|||
*/ |
|||
public class EmailTransmissionFrame extends JFrame { |
|||
// Private class to determine whether all the fields have appropriate |
|||
// input. This mediator will enable or disable the send button based on |
|||
// the input. |
|||
private class SendMediator implements DocumentListener { |
|||
public SendMediator() { |
|||
update(); |
|||
} |
|||
public void changedUpdate(DocumentEvent e) { |
|||
update(); |
|||
} |
|||
public void insertUpdate(DocumentEvent e) { |
|||
update(); |
|||
} |
|||
public void removeUpdate(DocumentEvent e) { |
|||
update(); |
|||
} |
|||
private void update() { |
|||
// this will ensure the buttons are not enabled unless |
|||
// the user has put input in all the fields. |
|||
boolean enabled = |
|||
toField.getText().trim().length() > 0 |
|||
&& fromField.getText().trim().length() > 0 |
|||
&& subjectField.getText().trim().length() > 0 |
|||
&& bodyArea.getText().trim().length() > 0; |
|||
sendButton.setEnabled(enabled); |
|||
sendMenuItem.setEnabled(enabled); |
|||
} |
|||
} |
|||
private JTextField toField = new JTextField(); |
|||
private JTextField fromField = new JTextField(); |
|||
private JTextField subjectField = new JTextField(); |
|||
private JTextArea bodyArea = new JTextArea(); |
|||
private JButton sendButton = new JButton("Send"); |
|||
private JMenuItem sendMenuItem = new JMenuItem("Send"); |
|||
public EmailTransmissionFrame() { |
|||
this.setLayout(new BorderLayout()); |
|||
//Build MenuBar |
|||
JMenuBar menuBar = new JMenuBar(); |
|||
JMenu menu = new JMenu("File"); |
|||
menu.add(sendMenuItem); |
|||
menuBar.add(menu); |
|||
this.setJMenuBar(menuBar); |
|||
//Make a panel object to reuse as we build the UI. |
|||
JPanel panel = new JPanel(); |
|||
//Create the top panel with details |
|||
panel.setLayout(new BorderLayout()); |
|||
panel.setBorder(BorderFactory.createTitledBorder("Details")); |
|||
panel.setLayout(new GridLayout(0,2)); |
|||
panel.add(new JLabel("To:")); |
|||
panel.add(toField); |
|||
panel.add(new JLabel("From:")); |
|||
panel.add(fromField); |
|||
panel.add(new JLabel("Subject:")); |
|||
panel.add(subjectField); |
|||
this.add(panel, BorderLayout.NORTH); |
|||
//Create the center panel with the textArea |
|||
panel = new JPanel(); |
|||
panel.setLayout(new BorderLayout()); |
|||
panel.add(new JScrollPane(bodyArea), BorderLayout.CENTER); |
|||
panel.setBorder(BorderFactory.createTitledBorder("Body")); |
|||
this.add(panel, BorderLayout.CENTER); |
|||
//Create the bottom panel. |
|||
panel = new JPanel(); |
|||
panel.setLayout(new FlowLayout()); |
|||
panel.add(sendButton); |
|||
this.add(panel, BorderLayout.SOUTH); |
|||
this.attachListeners(); |
|||
} |
|||
//This method will attach the mediator |
|||
private void attachListeners() { |
|||
DocumentListener dl = new SendMediator(); |
|||
toField.getDocument().addDocumentListener(dl); |
|||
fromField.getDocument().addDocumentListener(dl); |
|||
subjectField.getDocument().addDocumentListener(dl); |
|||
bodyArea.getDocument().addDocumentListener(dl); |
|||
ActionListener sendListener = new ActionListener() { |
|||
public void actionPerformed(ActionEvent e) { |
|||
dispose(); |
|||
} |
|||
}; |
|||
sendMenuItem.addActionListener(sendListener); |
|||
sendButton.addActionListener(sendListener); |
|||
} |
|||
private static void createAndShowGUI() { |
|||
EmailTransmissionFrame eframe = new EmailTransmissionFrame(); |
|||
eframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
|||
eframe.pack(); |
|||
eframe.setSize(600,600); |
|||
eframe.setLocationRelativeTo(null); |
|||
eframe.setVisible(true); |
|||
} |
|||
public static void main(String args[]) { |
|||
javax.swing.SwingUtilities.invokeLater(new Runnable() { |
|||
public void run() { |
|||
createAndShowGUI(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
</lang> |
|||
{{omit from|PARI/GP}} |
{{omit from|PARI/GP}} |
||
{{omit from|Retro}} |
{{omit from|Retro}} |
Revision as of 06:14, 30 December 2010
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
- define TASK_H
- 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 ;
} ;
- endif
</lang>
file task.cpp
<lang cpp>#include <QtGui>
- include <QString>
- 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>
- include "task.h"
int main( int argc, char *argv[ ] ) {
QApplication app( argc , argv ) ; MyWidget theWidget ; theWidget.show( ) ; return app.exec( ) ;
} </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
<lang tcl>package require Tk
- Model
set field 0
- 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]
- Controller
proc up {} {
global field incr field
} proc down {} {
global field incr field -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>
Java
This task is normally accomplished with a design pattern called the mediator.
<lang Java>
import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener;
/** * This frame allows a user to draft an email. * @author Ranman * @version Dec 30, 2010 */ public class EmailTransmissionFrame extends JFrame { // Private class to determine whether all the fields have appropriate // input. This mediator will enable or disable the send button based on // the input. private class SendMediator implements DocumentListener { public SendMediator() { update(); }
public void changedUpdate(DocumentEvent e) { update(); }
public void insertUpdate(DocumentEvent e) { update(); }
public void removeUpdate(DocumentEvent e) { update(); }
private void update() { // this will ensure the buttons are not enabled unless // the user has put input in all the fields. boolean enabled = toField.getText().trim().length() > 0 && fromField.getText().trim().length() > 0 && subjectField.getText().trim().length() > 0 && bodyArea.getText().trim().length() > 0; sendButton.setEnabled(enabled); sendMenuItem.setEnabled(enabled); } }
private JTextField toField = new JTextField(); private JTextField fromField = new JTextField(); private JTextField subjectField = new JTextField(); private JTextArea bodyArea = new JTextArea();
private JButton sendButton = new JButton("Send"); private JMenuItem sendMenuItem = new JMenuItem("Send");
public EmailTransmissionFrame() { this.setLayout(new BorderLayout());
//Build MenuBar JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menu.add(sendMenuItem); menuBar.add(menu); this.setJMenuBar(menuBar);
//Make a panel object to reuse as we build the UI. JPanel panel = new JPanel();
//Create the top panel with details panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createTitledBorder("Details")); panel.setLayout(new GridLayout(0,2)); panel.add(new JLabel("To:")); panel.add(toField); panel.add(new JLabel("From:")); panel.add(fromField); panel.add(new JLabel("Subject:")); panel.add(subjectField); this.add(panel, BorderLayout.NORTH);
//Create the center panel with the textArea panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(new JScrollPane(bodyArea), BorderLayout.CENTER); panel.setBorder(BorderFactory.createTitledBorder("Body")); this.add(panel, BorderLayout.CENTER);
//Create the bottom panel. panel = new JPanel(); panel.setLayout(new FlowLayout()); panel.add(sendButton); this.add(panel, BorderLayout.SOUTH);
this.attachListeners(); }
//This method will attach the mediator private void attachListeners() { DocumentListener dl = new SendMediator(); toField.getDocument().addDocumentListener(dl); fromField.getDocument().addDocumentListener(dl); subjectField.getDocument().addDocumentListener(dl); bodyArea.getDocument().addDocumentListener(dl);
ActionListener sendListener = new ActionListener() { public void actionPerformed(ActionEvent e) { dispose(); } };
sendMenuItem.addActionListener(sendListener); sendButton.addActionListener(sendListener); } private static void createAndShowGUI() { EmailTransmissionFrame eframe = new EmailTransmissionFrame(); eframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); eframe.pack(); eframe.setSize(600,600); eframe.setLocationRelativeTo(null); eframe.setVisible(true); } public static void main(String args[]) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } }
</lang>