15.35 Benutzerinteraktionen automatisieren
 
Eine besondere Eigenschaft von Präsentationsprogrammen ist, dass Benutzerinteraktionen wie von Zauberhand automatisch vom System vorgenommen werden. Ein Programm kann beispielsweise die Interaktion mit der Maus oder der Tastatur aufnehmen und zu einem späteren Zeitpunkt abspielen. Genau für diese Art der Oberflächensteuerung gibt es die Klasse Robot im Paket AWT. Sie verwaltet eine mit Aktionen gefüllte Ereigniswarteschlange. Die Aktionen werden nacheinander abgearbeitet.
|
void keyPress( int keycode )
Drückt eine Taste. |
|
void keyRelease( int keycode )
Lässt die Tasten wieder frei. |
|
void mouseMove( int x, int y )
Bewegt die Maus auf die Koordinate relativ zum aktuellen Fenster. |
|
void mousePress( int buttons )
Aktiviert eine oder mehrere Maustasten. |
|
void mouseRelease( int buttons )
Lässt die Maustaste wieder los. |
|
void delay( int ms )
Wartet Millisekunden. Mehr als 60 Sekunden sind nicht möglich, und das Resultat ist eine IllegalArgumentException. |
15.35.1 Automatisch in die Tasten hauen
 
Bei den Methoden keyPress(), keyRelease(), mousePress() beziehungsweise mouseRelease() sind die Parameter erklärungsbedürftig. Der Parameter keycode ist der erste, der beachtet werden muss.
Beispiel Der Roboter legt Tasten in die Ereignisschlange.
Robot rob = new Robot();
rob.keyPress( KeyEvent.VK_SHIFT );
rob.keyPress( KeyEvent.VK_U );
rob.keyPress( ’L’ ); rob.keyPress( ’L’ ); rob.keyPress( ’I’ );
rob.keyPress( ’0’ ); rob.keyPress( ’0’ ); rob.keyPress( ’7’ );
rob.keyRelease( KeyEvent.VK_SHIFT );
rob.keyPress( KeyEvent.VK_ENTER );
|
Das Argument von keyPress() und keyRelease() ist eine Konstante der Klasse KeyEvent. Neben den alphanumerischen Tasten auf der Tastatur finden sich unter anderem Konstanten für Funktionstasten, Metatasten, Cursortasten und weitere Sonderzeichen. Die Konstanten für Großbuchstaben und Ziffern decken sich mit den ASCII-Zeichen und können daher alternativ verwendet werden. Bei fast allen Sonderzeichen entspricht die Konstante dem ASCII-Code, jedoch ist dies nicht selbstverständlich. Die Kleinbuchstaben (ab 0x61) werden zum Beispiel auf den 10er-Block abgebildet (ab 0x60). Ist der übergebene Keycode undefiniert (durch die Konstante KeyEvent.VK_UNDEFINED vorgegeben), so erzeugt der Roboter eine IllegalArgumentException.
Um die Umschalttaste während der Automatisierung einzuschalten, nutzen wir keyPress(KeyEvent.VK_SHIFT) und lösen die Tasten mit keyRelease(). Das Beispiel macht deutlich, dass das Freigeben nur für Operationen wie zum Beispiel Shift, VK_ALT und VK_ALT_GRAPH nötig ist, allerdings nicht für normale Buchstaben.
Wird der obere Programmtext in einer grafischen Applikation genutzt, so muss sichergestellt sein, dass die Tasten auch die passende Komponente erwischen. Liegt der Fokus zum Beispiel auf einem Schieberegler, werden die Tastendrücke natürlich ohne Wirkung bleiben. Aus diesem Grund ist es angebracht, wieder mit requestFocus() den Fokus zu setzen.
15.35.2 Mausoperationen
 
Jetzt fehlen uns noch die Methoden mousePress() und mouseRelease(). Beide erhalten als Argument eine Konstante der Klasse InputEvent: entweder BUTTON1_MASK, BUTTON2_MASK oder BUTTON3_MASK oder eine Kombination aus den dreien wie BUTTON2_MASK | BUTTON3_MASK.
Beispiel Folgende Zeilen aktiviert die (meist linke) Maustaste, wartet eine Sekunde und lässt dann die Maustaste wieder los.
rob.mousePress( InputEvent.BUTTON1_MASK );
rob.delay( 1000 );
rob.mouseRelease( InputEvent.BUTTON1_MASK );
|
Wenn die Mausaktion eine Komponente treffen soll, dann gilt das Gleiche wie für Tastendrücke. Nur muss hier der Fokus nicht auf der Komponente liegen, sondern die Mauskoordinaten müssen auf die Komponente zeigen. Damit die Bildschirmkoordinate einer Komponente ausgelesen werden kann, bietet Component eine Methode getLocation OnScreen() an, die ein Point-Objekt mit den Startkoordinaten liefert. Diese können mit einem kleinen Offset an mouseMove() weitergereicht werden. Dann befindet sich der Zeiger über der Komponente und mousePress() kann seine Wirkung nicht mehr verfehlen.
15.35.3 Methoden zur Zeitsteuerung
 
|
void setAutoDelay( int ms )
Die Robot-Klasse sendet zum Abspielen Ereignisse an die Oberfläche. Diese Methode setzt die Dauer der Verzögerung fest, die nach dem Ereignis vergehen soll. Mehr als 60 Sekunden sind auch hier nicht gültig. |
|
int getAutoDelay()
Liefert die Zeit, die nach dem Ereignisaufruf vergehen soll. |
|
void waitForIdle()
Wartet, bis alle Ereignisse in der Warteschlange bearbeitet sind. |
|
void setAutoWaitForIdle( boolean isOn )
Setzt, ob waitForIdle() nach einem Ereignis aufgerufen wird. |
|
boolean isAutoWaitForIdle()
Liefert true, falls der Robot automatisch waitForIdle() nach einem Ereignis aufruft. |
15.35.4 Screenshots
 
Eine sehr interessante Funktion der Robot-Klasse ist createScreenCapture(). Mit ihr lässt sich ein Bildschirmabzug (Screenshot) machen. Die Funktion erwartet als Argument ein Rectangle-Objekt (speichert x, y, height und width), welches den zu »fotografierenden« Bereich spezifiziert. Das Eregbnis von createScreenCapture() ist ein BufferedImage-Objekt, das direkt über einen JPEGCodec in eine JPEG-Datei oder mit ImageIO in andere Formate geschrieben werden kann.
Listing 15.50
Screenshot.java
import com.sun.image.codec.jpeg.JPEGCodec;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
//import javax.imageio.ImageIO;
public class Screenshot
{
public static void main( String args[] ) throws Exception
{
Thread.sleep( 2000 );
BufferedImage bi = new Robot().createScreenCapture(
new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );
OutputStream out = new FileOutputStream( "c:/screenshot.jpg" );
JPEGCodec.createJPEGEncoder( out ).encode( bi );
out.close();
// oder unter 1.4 ImageIO.write( bi, "jpg", new File("screenshot.jpg") );
System.exit( 0 );
}
}
15.35.5 Funktionsweise und Beschränkungen
 
Für die Steuerung auf der Rechnerseite sind insbesondere unter Unix-Systemen beim netzwerkfähigen X-Window-System (X11) einige Anforderungen zu erfüllen. Hier sind Rechte erforderlich, um auf der unteren Ebene Ereignisse platzieren zu können. Ein X-Window-System benötigt hierfür die aktivierte Standarderweiterung XTEST 2.2. Verletzt die aktuelle Architektur diese Vorgaben, so wird während der Konstruktion eines Robot-Objekts eine AWTException ausgelöst.
15.35.6 Zeitliches Ausführen mit dem javax.swing.Timer
 
Der javax.swing.Timer ist eine einfache Variante von java.util.Timer zum Ausführen von Programmcode eines Action-Listeners. Im Konstruktor des Timer-Objekts wird für Intervalle die Verzögerungszeit eingestellt, wie im folgenden Beispiel, in dem jede Sekunde auf eine Komponente p ein repaint() eine Neudarstellung motiviert.
javax.swing.Timer t = new javax.swing.Timer( 1000, new ActionListener() {
public void actionPerformed( ActionEvent e ) {
p.repaint();
}
});
t.start(); // t.stop() beendet.
Vergleichbar mit dem Util-Timer kann der Swing-Timer Aufgaben auch nur einmal ausführen – der Konstruktor hält dafür unterschiedliche Parameter bereit.
15.35.7 MouseInfo und PointerInfo
 
Mit der Klasse Robot lassen sich zwar Tastatur- und Maus-Ereignisse produzieren und Screenshots nehmen, doch Informationen über die aktuelle Mausposition liefert die Klasse nicht. Auch das MouseEvent liefert nur die aktuellen relativen Koordinaten bei einem Fenster, jedoch nicht die Position der Maus auf dem physikalischen Schirm.
Seit Java 1.5 liefert die Klasse MouseInfo diese Information. Die statische Funktion MouseInfo.getPointerInfo() liefert ein PointerInfo-Objekt, welches Aussagen über die anzeigende Einheit gibt und über die Position des Mauszeigers. MouseInfo.getNumberOfButtons() liefert die Anzahl der Knöpfe.
PointerInfo info = MouseInfo.getPointerInfo();
System.out.println( info.getLocation() );
// java.awt.Point[x=633,y=482]
System.out.println( info.getDevice() );
// Win32GraphicsDevice[screen=0]
System.out.println( MouseInfo.getNumberOfButtons() ); // 5
|