# KeyListener implements Runnable



## Phaxx (25. Apr 2004)

Hallo, habe eine Frage zu folgendem Code:


```
public String readLine() throws InterruptedException{
	input.setEnabled(true); //input ist ein JTextField
	Thread keyListener = new Thread(new InputKeyListener());
	keyListener.join();
	input.setEnabled(false);
	return in;
}
```


```
class InputKeyListener extends KeyAdapter implements Runnable{
	public void keyPressed(KeyEvent ke){
		if(ke.getKeyCode()==KeyEvent.VK_ENTER){
			in = input.getText();
			input.setText("");
		}else if(ke.getKeyCode()==KeyEvent.VK_ESCAPE){
			System.exit(1);
		}
	}
	
	public void run(){
		input.addKeyListener(this);
	}
}
```
Wobei _InputKeyListener_ eine innere Klasse von der Klasse ist, welche auch _public String readLine()_ beinhaltet.

Jetzt erwarte ich, dass die Methode readLine() den Text zurückgibt, welcher im JTextField input eingegeben und mit ENTER abgeschlossen wird...
Allerdings wird *null* zurückgegeben...

Kann mir das jemand erklären?


----------



## Beni (25. Apr 2004)

1. startest du diesen Thread nie Thread#start
2. würde das nichts nützen, weil der Thread schnell fertig ist (KeyListener hinzufügen und weg...)

Du köntest ein Paar _Object#wait_ und _Object#notify_ oder _Thread#sleep_ und _Thread#interrupt_ verwenden, aber dann muss das readLine in einem eigenen Thread ablaufen.

Als Versuch:
	
	
	
	





```
readLine(){
  final Object LOCK = new Object();
  InputKeyListener input = InputKeyListener( LOCK );

  try{
    synchronized( LOCK ){
      LOCK.wait();
    }
  }
  catch( InterruptedException ex ){}

  return in;
}
```


```
public void keyPressed(KeyEvent ke){
      if(ke.getKeyCode()==KeyEvent.VK_ENTER){
         in = input.getText();
         input.setText("");

         synchronized( LOCK ){
            LOCK.notify();
         }
   }
```

Habs aber nicht getestet!

mfg Beni


----------



## Phaxx (26. Apr 2004)

Geht leider nicht, oder ich weiss nicht genau, wie du's meinst...

Die Methode keyPressed(KeyEvent ke){} ist in deinem Beispiel auch in einer eigenen Klasse InputKeyListener? Aber die ist nicht mehr "runnable", richtig? Wenn ja, dann gibts ein Problem bei 
	
	
	
	





```
InputKeyListener input = InputKeyListener(LOCK);
```
Da meldet er: "This method InputKeyListener(Object) is undefined for the type Hauptklasse"

Wenn ich da schreibe: 
	
	
	
	





```
InputKeyListener input = new InputKeyListener(LOCK);
```
 dann gibts natürlich einen Fehler, weil es keinen entsprechenden Konstruktor gibt...

Oder verstehe ich dich jetzt grundsätzlich falsch?


----------



## Beni (26. Apr 2004)

Hier ist ein Beispiel, wie ich das gemeint habe:

```
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JTextField;

public class Main{
    public static void main(String[] args){
        new Main();
    }
    
    private JFrame frame;
    private JTextField field;
    
    public Main(){
        frame = new JFrame( "Ich bin ein JFrame!" );
        frame.setBounds( 50, 50, 300, 100 );
        
        field = new JTextField();
        
        Object lock = new Object();
        Reader reader = new Reader( lock );
        InputKeyListener list = new InputKeyListener( lock );
        field.addKeyListener( list );
        
        frame.getContentPane().add( field );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        
        reader.start(); // Den Thread starten, der schlussendlich "readLine" aufrufen wird.
    }
    
    public String readLine( Object lock ){
        frame.setVisible( true );
        
        synchronized( lock ){
            try {
                lock.wait();
            } catch (InterruptedException e) {
                // silent
            }
        }
        
        frame.setVisible( false );
        return field.getText();
    }
    
    private class Reader implements Runnable{
        private Object lock;
        
        public Reader( Object lock ){
            this.lock = lock;
        }
        
        public void start(){
            new Thread( this ).start();
        }
        
        public void run(){
            System.out.println( "Thread gestartet" );
            System.out.println( "Line: " + readLine( lock ));
            System.out.println( "Thread beendet" );
        }
    }
    
    private class InputKeyListener extends KeyAdapter{
        private Object lock;
        
        public InputKeyListener( Object lock ){
            this.lock = lock;
        }
        
        public void keyPressed(KeyEvent ke){
            if(ke.getKeyCode()==KeyEvent.VK_ENTER){
               synchronized( lock ){
                   lock.notify();
               }
            }else if(ke.getKeyCode()==KeyEvent.VK_ESCAPE){
               System.exit(1);
            }
         } 
    }
}
```

mfg Beni


----------



## Phaxx (29. Apr 2004)

Also erstmals danke für dein Beispiel. Habe nun stundenlang rumprobiert und versucht, das ganze in mein Projekt einzugliedern, es will mir aber einfach nicht so ganz gelingen... Hier mal mein Code (ich weiss, ist ein wenig umfangreich):


```
/*
 * Created on 22.04.2004
 */
package Console;

/**
 * @author Michael Rechberger
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Console extends JFrame{
	Dimension screen;
	private JLabel output;
	private JTextField input;
	private String in;
	private Object lock;
	private Reader reader;
	private InputKeyListener list;
	
	
	public Console(int width, int height, String title){
		//JFrame erstellen
		super(title);
		screen = Toolkit.getDefaultToolkit().getScreenSize();
		setSize(width, height);
		setLocation(	(screen.width - getSize().width)/2,		//JFrame zentrieren
						(screen.height - getSize().height)/2 );
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//Container erstellen und Layout zuweisen
		Container c = getContentPane();
		c.setLayout(new BorderLayout());
		
		//JLabel erstellen
		output = new JLabel("<html></html>");
		JScrollPane scrollableOutput = new JScrollPane(output);
		c.add(scrollableOutput,BorderLayout.CENTER);
		output.setVerticalAlignment(JLabel.TOP);
		
		//JTextField erstellen
		input = new JTextField();
		c.add(input,BorderLayout.SOUTH);
		input.setEnabled(false);
		
		//Object erstellen für Mutlithreading
		//lock = new Object(); 
		//list = new InputKeyListener( lock );
		//input.addKeyListener(list);
		setVisible(true);
	}
	
	public Console(int width, int height){
		this(width,height,"Java Console");
	}
	
	public Console(){
		this(800,600);
	}
	
	public String readLine(){
		
		lock = new Object();
		list = new InputKeyListener( lock );
		input.addKeyListener(list);	
		reader = new Reader ( lock );

		
		//reader.start();
		
		println(in = input.getText());
		System.out.println(input.getText());
		input.setText("");
	
		return in;
	}
	
	public void read( Object lock ){
		input.setEnabled(true);
		System.out.println("1");
		synchronized( lock ){
			try{
				lock.wait();
			}catch (InterruptedException ex){
				System.out.println("wait interrupted");
			}
		}
		//input.setEnabled(false);
		}
	
	public void print(String Text){
		if(Text.equals("exit")){
			System.exit(0);
		}
		output.setText(output.getText().substring(0,output.getText().length()-7)+ Text + "</html>");
	}
	
	public void println(String Text){
		if(Text.equals("exit")){
			System.exit(0);
		}
		output.setText(output.getText().substring(0,output.getText().length()-7)+ Text + "
</html>");
	}
	
	public void println(){
		output.setText(output.getText().substring(0,output.getText().length()-7) + "
</html>");
	}

	private class InputKeyListener extends KeyAdapter{
		private Object lock;
		
		public InputKeyListener (Object lock){
			this.lock = lock;
		}
		
		public void keyPressed(KeyEvent ke){
			if(ke.getKeyCode()==KeyEvent.VK_ENTER){
				in = input.getText();
				synchronized(lock){
					lock.notify();
				}
			}else if(ke.getKeyCode()==KeyEvent.VK_ESCAPE){
				System.exit(1);
			}
		}
	}
	
	private class Reader implements Runnable{
		private Object lock;
		
		public Reader (Object lock){
			this.lock = lock;
			this.start();
		}
		
		public void start (){
			new Thread ( this ).start();
		}
		
		public void run (){
			read( lock );
		}
	}
}
```

Was ich hier eigentlich machen möchte, ist eine Swing-basierte Konsole. Ich weiss, macht nicht viel Sinn, mach dies aber einfach als kleine Übung. Nun möchte ich von das ganze so aufrufen können:

```
public class Main {
	public static void main(String[] args) throws IOException, InterruptedException{
		Console console = new Console();
		
		String in = console.readLine();
		console.println(in);
	}
}
```

Nun weiss ich nicht recht, wo ich jetzt in meiner Console-Class dass thread.wait() hinsetzen muss. Habe vieles versucht, aber habe es nie wirklich geschafft.

Zudem ist mir nicht ganz klar wie das mit dem Object lock funktioniert. Verstehe ich das richtig, wenn das einfach ein "Pseude-Object" ist, womit ich den Thread eindeutig ansprechen kann? Habe es auch in Tutz nachgelesen, meine Fragen wurden aber nicht wirklich geklärt.

Danke
Phaxx


----------



## Beni (29. Apr 2004)

Die Klasse "Reader" hab ich rausgeworfen, der Rest praktisch 1:1 übernommen.
Hat das jetzt das Verhalten, dass du dir wünschst?

```
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;

import javax.swing.*;

public class Console extends JFrame{
   Dimension screen;
   private JLabel output;
   private JTextField input;
   private String in;
   private Object lock;
   private InputKeyListener list;
   
   public static void main(String[] args) throws IOException, InterruptedException{
       Console console = new Console();
       
       for( int i = 0; i < 5; i++ ){
           console.println( i + ": Geben sie bitte etwas ein..." );
           String in = console.readLine();
           console.println( "Sie haben geschrieben: " + in);
       }
    } 
   
   public Console(int width, int height, String title){
      //JFrame erstellen
      super(title);
      screen = Toolkit.getDefaultToolkit().getScreenSize();
      setSize(width, height);
      setLocation(   (screen.width - getSize().width)/2,      //JFrame zentrieren
                  (screen.height - getSize().height)/2 );
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
      //Container erstellen und Layout zuweisen
      Container c = getContentPane();
      c.setLayout(new BorderLayout());
      
      //JLabel erstellen
      output = new JLabel("<html></html>");
      JScrollPane scrollableOutput = new JScrollPane(output);
      c.add(scrollableOutput,BorderLayout.CENTER);
      output.setVerticalAlignment(JLabel.TOP);
      
      //JTextField erstellen
      input = new JTextField();
      c.add(input,BorderLayout.SOUTH);
      input.setEnabled(false);
      
      //Object erstellen für Mutlithreading
      lock = new Object();     // <<<<<<<<<<<<<<<<<<<< die wieder hinein
      list = new InputKeyListener( lock );
      input.addKeyListener(list);
      setVisible(true);
   }
   
   public Console(int width, int height){
      this(width,height,"Java Console");
   }
   
   public Console(){
      this(800,600);
   }
   
   public String readLine(){
      
      //lock = new Object();  // <<<<<<<<<<<<<<<<<<<< die raus
      //list = new InputKeyListener( lock );
      //input.addKeyListener(list);   
      
      //reader.start();
      read( lock ); // <<<<<<<<<<<<<<<<<<<<<<<<<<<< hier aufrufen
      
      println(in = input.getText());
      System.out.println(input.getText());
      input.setText("");
   
      return in;
   }
   
   public void read( Object lock ){
      input.setEnabled(true);
      System.out.println("1");
      synchronized( lock ){
         try{
            lock.wait();
         }catch (InterruptedException ex){
            System.out.println("wait interrupted");
         }
      }
      input.setEnabled(false);
    }
   
   public void print(String Text){
      if(Text.equals("exit")){
         System.exit(0);
      }
      output.setText(output.getText().substring(0,output.getText().length()-7)+ Text + "</html>");
   }
   
   public void println(String Text){
      if(Text.equals("exit")){
         System.exit(0);
      }
      output.setText(output.getText().substring(0,output.getText().length()-7)+ Text + "
</html>");
   }
   
   public void println(){
      output.setText(output.getText().substring(0,output.getText().length()-7) + "
</html>");
   }

   private class InputKeyListener extends KeyAdapter{
      private Object lock;
      
      public InputKeyListener (Object lock){
         this.lock = lock;
      }
      
      public void keyPressed(KeyEvent ke){
         if(ke.getKeyCode()==KeyEvent.VK_ENTER){
            in = input.getText();
            synchronized(lock){
               lock.notify();
            }
         }else if(ke.getKeyCode()==KeyEvent.VK_ESCAPE){
            System.exit(1);
         }
      }
   }
}
```

mfg Beni


----------



## Phaxx (29. Apr 2004)

Juhuuu! Wunderbar! Vielen Dank...

Jetzt nochmals eine Frage zum Object lock. Sehe ich das richtig, wenn dieses einfach als Object gebraucht wird, welches in diesem Thread läuft. Mit lock.wait() wird dann dieser Thread angewiesen zu warten, bis mit [ENTER] der Thread mit notify wieder geweckt wird?

Das ganze läuft also in einem einzigen Thread ab?

Wenn das so stimmt, wieso kann ich nicht anstelle vom Object lock mit "this" arbeiten? Habs versucht, ging aber nicht, oder ich habs falsch gemacht.


----------



## Beni (29. Apr 2004)

Grundsätzlich hast du alles verstanden.

Allerdings sind es immer noch zwei Threads: einer der durch die Main-Methode kommt, und der AWT-Thread, der für alle GUI-Elemente zuständig ist.

Es wäre schon möglich, _this_ zu benutzen, aber dann besteht die (nicht zu unterschätzende) Gefahr, dass sonst noch jemand "wait" und/oder "notify" benutzt (oder auch "synchronized", was eine ähnliche Wirkung hat), und das würde doch einen ziemlichen Durcheinander erzeugen.

mfg Beni


----------



## Phaxx (30. Apr 2004)

Hmm... dann hab ich's glaub trotzdem noch nicht ganz verstanden...  :roll:   
Also, das Object lock und die console laufen ja im selben Thread, oder?

Und .wait() bezieht sich doch auf den Thread, nicht auf's Object, nicht? Aber dann hätte ja this.wait() und lock.wait()grundsätzlich dieselbe Wirkung, nämlich dass der Thread, welcher diese beide Objects beinhaltet in die Pause geschickt wird...?

Wo hängt denn da bei mir der Knoten??  :?  :lol:


----------



## Beni (30. Apr 2004)

Phaxx hat gesagt.:
			
		

> Hmm... dann hab ich's glaub trotzdem noch nicht ganz verstanden...  :roll:
> Also, das Object lock und die console laufen ja im selben Thread, oder?



Nein (sonst würde das ganze ja nicht funktionieren). Sobald das Frame aufgeht, startet ein neuer Thread, der die Console übernimmt.



			
				Phaxx hat gesagt.:
			
		

> Und .wait() bezieht sich doch auf den Thread, nicht auf's Object, nicht? Aber dann hätte ja this.wait() und lock.wait()grundsätzlich dieselbe Wirkung, nämlich dass der Thread, welcher diese beide Objects beinhaltet in die Pause geschickt wird...?


Es bezieht sich auf den Thread (den es anhaltet) und auf das Object (welches als einziges Object den Thread weiterlaufen lassen kann (abgesehen von Thread#interrupt...)).
Die Wirkung wäre schon dieselbe, aber jeder könnte das weitere Verhalten beeinflussen. Denn mit dem "lock" kannst nur *Du* das "wait" beenden, mit dem "this" kann *jeder* das "wait" beenden.

mfg Beni


----------



## Phaxx (1. Mai 2004)

Ok, ich glaub, ich habs so einigermassen kapiert!
Vielen Dank für die Hilfe!

Phaxx


----------

