/* Applet.java Created on 25. Dezember 2004, 22:02
* @author pascal
*
* der client randomiziert die abfolge der antwort-wavs und setzt die reihenfolge am ende wieder zurück, damit
* die ergebnisse verglichen werden können. richtig schön ist das applet nicht programmiert: das ding ist zu
* monolithisch. es lässt sich aber recht einfach z.b. über die änderung des html-parameters "Fragenanzahl"
* an eigene bedürfnisse anpassen. Ansonsten bleibt das vorhaben: eine woche zeit
* würde wohl benötigt, um ein kleines layout-wysiwyg zu basteln, mit dem sich online-experimente, die so
* ähnlich sind wie das gerade besprochene, auch von einem laien erstellt werden könnten.
*/
import java.applet.AudioClip;
import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.AccessControlException;
public class AbfrageClient extends java.applet.Applet implements ActionListener {
// werte werden in der init vom html-dok () bezogen
private int anzahlFragen,anzahlAntworten,serverport;
String hostname;
int fragenNummer; // aktuelle frage
int antwortNummer; // aktuelle antwort
// die ergebnis-matrix.
// in [0][][] stehn die randomisierten sounds, in [1][][] die ergebnisse
int[][][] ergebnisse;// = new int[2][anzahlFragen][anzahlAntworten];
Color[] c = new Color[5];//degree of evaluation
Checkbox checkbox[][]; //= new Checkbox[anzahlAntworten][c.length];
// das datenfeld für den fragebogen
String fragebogen;
// erster Text, vor dem drücken des "persönl. daten senden"-knopfes
String fragebogenTexte = new String(
"Name: \nGeschlecht(w/m): \nAlter: \nIst Deutsch Deine Muttersprache(j/n): \nWenn ja, in welcher Region Deutschlands bist du aufgewachsen ?: \nKlick auf 'Persönliche Daten senden' wenn Du alles beantwortet hast.");
// hilfstext während des experiments
String iniText = new String(
" Willkommen zum Intonationsexperiment v2.3 ! \nKlicke auf die aktuelle Frage, um die Frage zu hören.\nBewerte jede der 5 Antworten: wie hört sich die Betonung\nder jeweiligen Antwort auf die Frage an?\nDrücke die Knöpfe neben den Antworten:\nVon dunkelrot='passt überhaupt nicht' bis dunkelgrün='passt sehr gut'.\n\nIst alles zu Deiner Zufriedenheit bewertet, drücke den 'senden'-Knopf.");
// hilfstext beim Probedurchlauf
String probeText = new String(
"Bevor das Experiment beginnt, erfolgt ein Probedurchlauf,\num Dich mit der Bedienung des Programms vertraut zu machen.\nSchritt 1: Höre dir die Probefrage an: klicke auf 'Probefrage'.\nSchritt 2: Höre Dir die Antwort1 an: klicke den Knopf 'Antwort1'.\nBewerte sie durch Anklicken der farbigen Felder:\nRot=passt überhaupt nicht bis Dunkelgrün=passt sehr gut.\nSchritt 3: Höre Dir Antwort2 an und bewerte sie usw... \nWenn Du alle 5 Antworten bewertet hast beginnt das Experiment.\nKlicke 'weiter'.\nWenn Du Dir Deine vorigen Bewertungen nochmal anschauen und ggf.\nändern willst, verwende den Knopf 'zurück'.");
// sound handling
AudioClip audioClipAntworten[]; //= new AudioClip[anzahlAntworten];
AudioClip audioClipFragen[];// = new AudioClip[anzahlFragen];
static GridBagConstraints gridBagConstraints = new GridBagConstraints();
Panel interactionPanel = new Panel();
Panel navigationPanel = new Panel();
TextArea textPanel = new TextArea();
Panel abfragePanel = new Panel();
Panel fragePanel = new Panel();
Panel antwortPanel = new Panel();
Panel checkPanel = new Panel();
Button zurück = new Button("<< zurück"); // frage--
Button weiter = new Button("weiter >>"); // frage ++
Button pdatenSenden = new Button("Persönliche Daten senden");
Button senden = new Button("senden"); // für auswertung
Button frage[];// = new Button[anzahlFragen];
Button antwort[];// = new Button[anzahlAntworten];
Frame buttonFrame = new Frame();
/**
* Initialization method that will be called after the applet is loaded into the browser.
*/
public void init() {
// werte werden aus dem html-dok geladen ()
hostname =getParameter("Hostname");
serverport=Integer.valueOf(getParameter("Serverport")).intValue();
anzahlFragen = Integer.valueOf(getParameter("Fragenanzahl")).intValue()+1;
anzahlAntworten = Integer.valueOf(getParameter("Antwortanzahl")).intValue();
ergebnisse = new int[2][anzahlFragen][anzahlAntworten];
checkbox = new Checkbox[anzahlAntworten][c.length];
audioClipAntworten = new AudioClip[anzahlAntworten];
audioClipFragen = new AudioClip[anzahlFragen];
frage = new Button[anzahlFragen];
antwort = new Button[anzahlAntworten];
// farben der 5 checkboxen
c[0] = new Color(255, 0, 0);
c[1] = new Color(255, 127, 0);
c[2] = new Color(255, 255, 0);
c[3] = new Color(0, 255, 0);
c[4] = new Color(0, 210, 0);
fragenNummer = 0; // aktuelle frage
antwortNummer = 0;// aktuelle antwort
// verschachteltes layout: erstmal 2 zeilen in einer spalte
setLayout(new GridLayout(2, 1));
// interactionPanel: 2 zeilen in einer spalte: buttons und textarea
interactionPanel.setLayout(new GridBagLayout());
interactionPanel.setBackground(Color.orange);
// das frage-antwort-Panel: eine Zeile mit drei spalten
abfragePanel.setLayout(new GridLayout(1, 3));
abfragePanel.setBackground(Color.orange);
// das antwortPanel bekommt das layout: maxZeilen, 1 spalte
fragePanel.setLayout(new GridLayout(anzahlFragen, 1));
// das antwortPanel bekommt das layout: antwortZeilen, 1 spalten
antwortPanel.setLayout(new GridLayout(anzahlAntworten, 1));
// das antwortPanel bekommt das layout: antworten, degreeZeilen
checkPanel.setLayout(new GridLayout(anzahlAntworten, c.length));
// die panels werden sequentiell in das übergeorndete layout gelegt
// setze das panel auf den container
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.fill = GridBagConstraints.NORTH;
interactionPanel.add(navigationPanel, gridBagConstraints);
interactionPanel.add(navigationPanel);
textPanel.setBounds(10, 10, 300, 300);
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = GridBagConstraints.BOTH;
interactionPanel.add(textPanel, gridBagConstraints);
// interactionPanel.add(textPanel);
add(interactionPanel);
add(abfragePanel);
abfragePanel.add(fragePanel);
abfragePanel.add(antwortPanel);
abfragePanel.add(checkPanel);
// setzen der buttons auf die panels
navigationPanel.add(weiter);
navigationPanel.add(zurück);
navigationPanel.add(senden);
navigationPanel.add(pdatenSenden);
//erstmal disable, wegen datenerhebung
// erst fragebogen ausfüllen !
zurück.setEnabled(false);
weiter.setEnabled(false);
senden.setEnabled(false);
for (int i = 0; i < anzahlFragen; i++) {
if (i == 0) {// ermöglicht Probe-Knopf
frage[i] = new Button("Probefrage");
audioClipFragen[i] = getAudioClip(getDocumentBase(), "f1.wav");
} else {
frage[i] = new Button("Frage " + (i));
audioClipFragen[i] = getAudioClip(getDocumentBase(), "f" + (i) + ".wav");
}
frage[i].addActionListener(this);
frage[i].setEnabled(false);
frage[i].setBackground(Color.cyan);
fragePanel.add(frage[i]);
// ergebnisse werden auf default gesetzt
// der max+1 wert ist default => wenn gesetzt, zeigt nichts an
for (int k = 0; k < anzahlAntworten; k++) {
ergebnisse[1][i][k] = c.length + 1;
ergebnisse[0][i][k] = k;
}
randomizeErgebnisse(i);
}
for (int i = 0; i < anzahlAntworten; i++) {
antwort[i] = new Button("Antwort " + (i + 1));
antwort[i].setBounds(0, 0, 100, 50);
antwort[i].addActionListener(this);
antwort[i].setEnabled(true);
antwort[i].setBackground(Color.orange);
antwortPanel.add(antwort[i]);
audioClipAntworten[i] = getAudioClip(getDocumentBase(), "a" + (i + 1)
+ ".wav");
for (int k = 0; k < c.length; k++) {
checkbox[i][k] = new Checkbox();
checkbox[i][k].setState(false);
checkbox[i][k].setBackground(c[k]);
checkPanel.add(checkbox[i][k]);
}
}
// setzen der actionListener
pdatenSenden.addActionListener(this);
weiter.addActionListener(this);
senden.addActionListener(this);
zurück.addActionListener(this);
// ein paar einstellungen
//hintergrund weiss zeichnen
setBackground(Color.white);
textPanel.setText(fragebogenTexte);
}
public void actionPerformed(ActionEvent ae) { // ae ist variable typs ActionEvent
// abspielen der wavs. assoziert mit den 2 knopf-arten.
for (int i = 0; i < anzahlAntworten; i++) {
if (ae.getSource() == antwort[i]) {
// [0] speichert die randomisierte reihenfolge der antwortsounds
audioClipAntworten[ergebnisse[0][fragenNummer][i]].play();
}
}
for (int i = 0; i < anzahlFragen; i++) {
if (ae.getSource() == frage[i]) {
audioClipFragen[i].play();
}
}
// knopf "weiter" gedrückt
if ((ae.getSource() == weiter) && (fragenNummer < anzahlFragen)) {
// es darf nur ein checkbox getickt sein !
if (vieleKreuze())
return;
speicherErgebnisse();// auswertungen speichern
// checkBoxen();//reconstruct
// zur näxten frage, wenn alle antworten bewertet wurden ...
// ... UND nicht letzte frage !
if (fragenNummer < anzahlFragen - 1) {
//check it out ... vollständig ?
for (int k = 0; k < anzahlAntworten; k++) { // k= antwort
if (ergebnisse[1][fragenNummer][k] > c.length) {
textPanel.setText("Bewerte bitte die Antwort " + (k + 1) + "...");
antwortNummer = k;
return;
}
} // wenn also vollständig:
speicherErgebnisse();// auswertungen speichern
setAllCheckboxenFalse(); // setze die checkboxen zurück
frage[fragenNummer].setEnabled(false);
fragenNummer = fragenNummer + 1;
frage[fragenNummer].setEnabled(true);
antwortNummer = 0;
checkBoxen(); // checkboxenrekonstruktion
textPanel.setText(iniText);
return;
}
if (fragenNummer == 0)
textPanel.setText(probeText);
else
textPanel.setText(iniText);
}
//knopf "persönliche daten senden" gedrückt (Anfangs soll der Fragebogen beantwortet werden...)
if ((ae.getSource() == pdatenSenden)) {
fragebogen = new String(textPanel.getText());
frage[0].setEnabled(true); // die ersten knöpfe freischalten
weiter.setEnabled(true);
zurück.setEnabled(true);
senden.setEnabled(true);
pdatenSenden.setEnabled(false);
textPanel.setEditable(false); // ab jetzt nicht mehr editierbar
textPanel.setText(probeText);
}
// knopf "zurück" gedrückt
if ((ae.getSource() == zurück)) {
// es darf nur ein checkbox getickt sein !
if (vieleKreuze())
return;
// auswertungen speichern & zurücksetzen der checkboxen
if (fragenNummer > 0) { // pass auf die arraygröße auf!
speicherErgebnisse();
setAllCheckboxenFalse();
frage[fragenNummer].setEnabled(false);
fragenNummer = fragenNummer - 1;
frage[fragenNummer].setEnabled(true);
antwortNummer = anzahlAntworten - 1; // höchster array-eintrag
checkBoxen();
textPanel.setText(iniText);
return;
}
checkBoxen();
if (fragenNummer == 0)
textPanel.setText(probeText);
else
textPanel.setText(iniText);
}
// knopf "senden" gedrückt
if (ae.getSource() == senden) {
if (vieleKreuze())
return;
speicherErgebnisse();
// checken , ob alle fragen schon beantowrtet
for (int i = 1; i < anzahlFragen; i++) { // i = frage
// ist eine frage noch unbeantwortet ? 0 ist Probe, deshalb i=1
for (int k = 0; k < anzahlAntworten; k++) { // k= antwort
if (ergebnisse[1][i][k] > c.length) {
textPanel
.setText("Bewerte bitte erst alle 5 Antworten zur Frage "
+ (i));
checkBoxen();
return;
}
}
}
// erleichert hinterher das parsen. ist so ein tag...
StringBuffer sendeDaten = new StringBuffer(fragebogen + "\n");
// dekodieren
// => reihenfolge der antworten in dem array passen zu dem des wav-arrays
int[][][] tmpArray = new int[2][anzahlFragen][anzahlAntworten];
for (int i = 1; i < anzahlFragen; i++) { // alle fragen . 0 ist Probe, deshalb i=1
sendeDaten.append("\n");
for (int k = 0; k < anzahlAntworten; k++) { // simpler algorithmus zum...
for (int h = 0; h < anzahlAntworten; h++) {
if (ergebnisse[0][i][h] == k) { // ...reihenfolge finden: 0,1,2...
tmpArray[1][i][k] = ergebnisse[1][i][h] + 1; //...kopieren
// das "+1" ist intuitiver zu lesen (arrays fangen eben bei 0 an)
sendeDaten.append(tmpArray[1][i][k]);
}
}
}
sendeDaten.append("
"); // zeilenumbruch nach jeder datenreihe
}
sendeDaten.append("\n");
try { // senden der daten
Socket socket = new Socket(hostname, serverport);
// Socket socket = new Socket("127.0.0.1", 7893);
senden.setEnabled(false);
textPanel.setText("Habe bitte etwas Geduld: Daten werden gesendet...");
socket.getOutputStream().write((sendeDaten.toString()).getBytes());
socket.close();
textPanel
.setText("Danke, dass Du am Intonationsexperiment teilgenommen hast. \nDie Daten wurden gesendet.\nWenn Du noch einen Intonationstest machen willst,\ndann drücke den 'reload' Knopf Deines Browsers\n(das grüne recycling-zeichen)");
}
catch (UnknownHostException e) {
textPanel
.setText("Fehler: \nServer down ? \n sorry, die ergebnisse konnten nicht gesendet werden ...\nunsere schuld..."
+ e.getMessage()
+ "\n\nDamit Deine Daten nicht verloren gehen, sende sie uns per email.\n Hier die Daten:"
+ sendeDaten + "\nemail an: pascal.christop hweb.de");
} catch (IOException e) {
textPanel
.setText("IO-Fehler: \nsorry, die ergebnisse konnten nicht gesendet werden ...\nunsere schuld..."
+ e.getMessage()
+ "\n\nDamit Deine Daten nicht verloren gehen, sende sie uns doch bitte per email.\n Hier die Daten:"
+ sendeDaten + "\nemail an: pascal.christoph web.de");
} catch (AccessControlException e) {
textPanel
.setText("AccessControlException: \nsorry, die ergebnisse konnten nicht gesendet werden ...\nunsere schuld..."
+ e.getMessage()
+ "\n\nDamit Deine Daten nicht verloren gehen, sende sie uns doch bitte per email.\n Hier die Daten:"
+ sendeDaten
+ "\nSchicke die Daten an: pascal.christoph web.de");
}
senden.setEnabled(false);
}
}
// schon mal hier gewesen => rekonstruiere checkbox
// default (also "ungecheckt") war der Wert fragenMax+1 (der ja nie
// vorkommen kann)
// fragenNummer = y = aktuelle zeile
// fragenMax = x Max = spalten MAx
void checkBoxen() {
for (int i = 0; i < anzahlAntworten; i++) { // antworten
if (ergebnisse[1][fragenNummer][i] < c.length + 1) {
checkbox[i][ergebnisse[1][fragenNummer][i]].setState(true);
}
}
}
// die ergbenisse werden im ergebnis-array gespeichert
void speicherErgebnisse() { // fragenNumnmer = globale, aktuelle frage
for (int k = 0; k < anzahlAntworten; k++) { // zeilen=antworten
ergebnisse[1][fragenNummer][k] = c.length + 1;//alles default setzten
for (int i = 0; i < c.length; i++) { // spalten=chekcboxenNr.
if (checkbox[k][i].getState()) {
// zur aktuellen frage die antwort speichern
// z.B. Antwort k hat 3. checkbox => speichere 3
ergebnisse[1][fragenNummer][k] = i;
}
}
}
return;
}
// rücksetzen der getickten checkboxen
void setAllCheckboxenFalse() {
for (int k = 0; k < anzahlAntworten; k++) { // zeilen=antworten
for (int i = 0; i < c.length; i++) { // spalten=chekcboxenNr.
if (checkbox[k][i].getState()) {
//löschen der ticks
checkbox[k][i].setState(false);
}
}
}
}
// es darf nur ein checkbox getickt sein !
boolean vieleKreuze() {
// äußere schleife: alle Fragen
for (int i = 0; i < anzahlAntworten; i++) {
// innere schleife: alle checkboxen einer Antwort
for (int k = 0; k < c.length; k++) {
if (checkbox[i][k].getState()) {
// vorgucken und vergelichen: nur eine checkbox getickt?
for (int j = k + 1; j < c.length; j++) {
// wenn mehrere, alle löschen
if (checkbox[i][j].getState()) { // checkboxen sind temporär gültig
for (int p = 0; p < c.length; p++) {
checkbox[i][p].setState(false);
}
ergebnisse[1][fragenNummer][k] = anzahlAntworten + 1; // alle auf default
textPanel.setText("JedeR nur EIN Kreuz !");
return true;
}
}
}
}
}
return false;
}
// zufallswert "vonZelle" und "nachZelle" für das arraycopy
void randomizeErgebnisse(int fragenNr) {
// p ist probability-variable
int p = anzahlAntworten + 1;
int vonZelle, nachZelle, tmp;
for (int i = 0; i < anzahlAntworten; i++) {
while (p >= anzahlAntworten)
p = (int) (Math.random() * 10);
vonZelle = p;
p = anzahlAntworten + 1;
while (p >= anzahlAntworten)
p = (int) (Math.random() * 10);
nachZelle = p;
tmp = ergebnisse[0][fragenNr][nachZelle];
ergebnisse[0][fragenNr][nachZelle] = ergebnisse[0][fragenNr][vonZelle];
ergebnisse[0][fragenNr][vonZelle] = tmp;
}
}
}