JavaFX KeyEvents (warum geht dat nit)

takidoso

Erfahrenes Mitglied
Hallo und Halli,
Irgendwie verstehe ich trotz eines plakativen Beispiels das Key-Eventhandling icht.
ich habe dazu einerseits diese Link, das Beispiel funktiniert einbahnfrei...
http://docs.oracle.com/javafx/2/events/handlers.htm#JFHHDBAC

Zum Anderen habe ich eine nette andere Beispielanwendung (auch nur aus dem Netz), welche ich erweitern möchte um die Möglichkeit eines Keyboardunterstützten Zoom.
Debugging und Ausgaben auf Konsole verraten mir, dass der Eventhandler noch nicht einmal ausgeführt wird :confused:
(Meine Versuche machte ich mit setOnKeyPressed, setOnKeyReleased und setOnKeyTyped; s. Zeile 76-100)
Java:
/*
 * Copyright 2013 Michael Heinrichs, http://netopyr.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.netopyr.javafx.ik.samples;

import com.netopyr.javafx.ik.Bone;
import com.netopyr.javafx.ik.Skeleton;
import com.netopyr.javafx.ik.com.netopyr.javafx.ik.transitions.TranslateHeadTransition;
import javafx.animation.Animation;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.paint.RadialGradientBuilder;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Caterpillar extends Application
{

	private static final int	WIDTH		= 1280;
	private static final int	HEIGHT		= 800;
	private static final double	SLOWDOWN	= 2;
	private Animation			runningAnimation;
	

	@Override
	public void start(Stage stage) throws Exception
	{
		final Skeleton caterpillar = createCaterpillar(50);
		final Bone head = caterpillar.getBones().get(0);
		final Node background = new Rectangle(WIDTH, HEIGHT, Color.BLACK);
		background.setOnMousePressed(new EventHandler<MouseEvent>()
		{

			@Override
			public void handle(MouseEvent mouseEvent)
			{
				final Point2D current = caterpillar.localToScene(head.getCurrentHead());
				final double targetX = mouseEvent.getSceneX();
				final double targetY = mouseEvent.getSceneY();
				final Duration duration = Duration.millis(SLOWDOWN * current.distance(targetX, targetY));
				if (runningAnimation != null)
				{
					runningAnimation.stop();
				}
				runningAnimation = new TranslateHeadTransition(caterpillar.getBones().get(0), duration, targetX - current.getX(), targetY
						- current.getY());
				runningAnimation.play();
			}
		});
		final Parent root = new Group(background, caterpillar);
		
		root.setOnKeyReleased(new EventHandler<KeyEvent>()
		{
			public void handle(KeyEvent keyEvent)
			{
				System.out.println(keyEvent.toString());
				switch (keyEvent.getCharacter())
				{
					case "z":
						root.setScaleX(root.getScaleX()+1);
						root.setScaleY(root.getScaleY()+1);
						
						break;
					
					case "Z":
						root.setScaleX(root.getScaleX()-1);
						root.setScaleY(root.getScaleY()-1);
						break;
					
					default:
						System.out.println(keyEvent.getCharacter());
				}
				
				
			}
		});
		
		final Scene scene = new Scene(root, WIDTH, HEIGHT);
		stage.setTitle("Caterpillar Sample");
		stage.setScene(scene);
		stage.show();
	}

	private Skeleton createCaterpillar(int length)
	{
		final Skeleton skeleton = new Skeleton();
		skeleton.setTranslateX(WIDTH / 4);
		skeleton.setTranslateY(HEIGHT / 2);
		final Bone head = new Bone(20);
		head.setSkeleton(skeleton);
		head.getContent().add(
				new Circle(10, RadialGradientBuilder.create().centerX(0.25).centerY(0.25).radius(1)
						.stops(new Stop(0.0, Color.RED), new Stop(1.0, Color.DARKRED)).build()));
		Bone iterator = head;
		final Paint fill = RadialGradientBuilder.create().centerX(0.25).centerY(0.25).radius(1)
				.stops(new Stop(0.0, Color.LIGHTGREEN), new Stop(1.0, Color.GREEN)).build();
		for (int i = 0; i < length; i++)
		{
			final Bone bone = new Bone(20, -60, 60);
			bone.getContent().add(new Circle(10, fill));
			iterator.getChildren().add(bone);
			iterator = bone;
		}
		return skeleton;
	}

	public static void main(String... args)
	{
		launch(args);
	}
}

Hier der Link vom Originalautor mit der Möglichkeit des Downloads der Sourcen
http://blog.netopyr.com/2013/03/06/javafx-library-for-inverse-kinematics-2-0/

Was mache ich da nur falsch?

Takidoso
 

takidoso

Erfahrenes Mitglied
Nun weiß ich warum es nicht klappt. die Nodes, die lediglich Grafik darstellen (z.B: Shapes) haben offenbar nun mal keinen KeyEvent verarbeitenden Mechanismus. In einem anderen Beispiel - diesmal von Oracle selbst - werden hierzu extra Panels verwendet um auf Tasten zu reagieren.
Meine Lösung, um auf Tasten zu reagieren, ist den stage selbst mit dem KeyHandler zu erfreuen.

guckst Du:
Java:
...
		stage.addEventHandler(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>()
		{

			public void handle(KeyEvent keyEvent)
			{
				System.out.println(keyEvent.toString());
				System.out.println("character='" + keyEvent.getCharacter() + "'");
				switch (keyEvent.getCharacter())
				{
					case "z" :
						System.out.println("try to zoom out");
						root.setScaleX(root.getScaleX() + .1);
						root.setScaleY(root.getScaleY() + .1);
						break;
					case "Z" :
						System.out.println("try to zoom in");
						root.setScaleX(root.getScaleX() - .1);
						root.setScaleY(root.getScaleY() - .1);
						break;
					default :
						System.out.println("other key:'" + keyEvent.getCharacter() + "'");
				}
			}
		});
...

Nun habe ich außerdem versucht das Zoomen mit dem Mausrad hinzubekommen. Leider nur mit sehr wenig Erfolg. Dafür verwendet man den Scroll-Event. Ich möchte, wenn der mittlere Mausknopf gedrückt ist (also das Mausrad) die Bewegungen des Mausrades als Zoom-Aufforderung verwenden.

Momentane leider nicht wirklich funktionierende Lösung ist diese:
Java:
/*
 * Copyright 2013 Michael Heinrichs, http://netopyr.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.netopyr.javafx.ik.samples;

import com.netopyr.javafx.ik.Bone;
import com.netopyr.javafx.ik.Skeleton;
import com.netopyr.javafx.ik.transitions.TranslateHeadTransition;

import javafx.animation.Animation;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.paint.RadialGradientBuilder;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Caterpillar extends Application
{

	private static final int	WIDTH					= 1280;
	private static final int	HEIGHT					= 800;
	private static final double	SLOWDOWN				= 10;
	private Animation			runningAnimation;
	private boolean				m_mouseMiddleButtonDown	= false;

	@Override
	public void start(Stage stage) throws Exception
	{
		final Skeleton caterpillar = createCaterpillar(50);
		final Bone head = caterpillar.getBones().get(0);
		final Node background = new Rectangle(WIDTH, HEIGHT, Color.BLACK);
		final Parent root = new Group(background, caterpillar);
		root.setOnMousePressed(new EventHandler<MouseEvent>()
		{

			@Override
			public void handle(MouseEvent mouseEvent)
			{
				if (mouseEvent.isMiddleButtonDown())
				{
					System.out.println("MiddleButton is down");
					m_mouseMiddleButtonDown = true;
				}
			}
		});
		root.setOnMouseReleased(new EventHandler<MouseEvent>()
		{

			@Override
			public void handle(MouseEvent mouseEvent)
			{
				System.out.println("MiddleButton is up");
				if (m_mouseMiddleButtonDown)
				{
					m_mouseMiddleButtonDown = false;
				}
				else
				{
					if (mouseEvent.isAltDown())
					{
						System.out.println("Alt down:");
						root.setScaleX(root.getScaleX() + .1);
						root.setScaleY(root.getScaleY() + .1);
					}
					else if (mouseEvent.isShiftDown())
					{
						System.out.println("Shift down:");
						root.setScaleX(root.getScaleX() - .1);
						root.setScaleY(root.getScaleY() - .1);
					}
					else
					{
						final Point2D current   = caterpillar.localToScene(head.getCurrentHead());
						final double targetX    = mouseEvent.getSceneX();
						final double targetY    = mouseEvent.getSceneY();
						final Duration duration = Duration.millis(SLOWDOWN * current.distance(targetX, targetY) / root.getScaleX());
						if (runningAnimation != null)
						{
							runningAnimation.stop();
						}
						runningAnimation = new TranslateHeadTransition(caterpillar.getBones().get(0), duration, targetX / root.getScaleX()
								- current.getX() / root.getScaleX(), targetY / root.getScaleY() - current.getY() / root.getScaleY());
						runningAnimation.play();
					}
				}
			}
		});
		stage.addEventHandler(ScrollEvent.SCROLL, new EventHandler<ScrollEvent>()
		{

			public void handle(ScrollEvent scrollEvent)
			{
				System.out.println(scrollEvent.toString());
				if (m_mouseMiddleButtonDown)
				{
					root.setScaleX(root.getScaleX() + scrollEvent.getDeltaY() > 0 ? +.1 : -.1);
					root.setScaleY(root.getScaleY() + scrollEvent.getDeltaY() > 0 ? +.1 : -.1);
				}
			}
		});
		stage.addEventHandler(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>()
		{

			public void handle(KeyEvent keyEvent)
			{
				System.out.println(keyEvent.toString());
				System.out.println("character='" + keyEvent.getCharacter() + "'");
				switch (keyEvent.getCharacter())
				{
					case "z" :
						System.out.println("try to zoom out");
						root.setScaleX(root.getScaleX() + .1);
						root.setScaleY(root.getScaleY() + .1);
						break;
					case "Z" :
						System.out.println("try to zoom in");
						root.setScaleX(root.getScaleX() - .1);
						root.setScaleY(root.getScaleY() - .1);
						break;
					default :
						System.out.println("other key:'" + keyEvent.getCharacter() + "'");
				}
			}
		});
		final Scene scene = new Scene(root, WIDTH, HEIGHT);
		stage.setTitle("Caterpillar Sample");
		stage.setScene(scene);
		stage.show();
	}

	private Skeleton createCaterpillar(int length)
	{
		final Skeleton skeleton = new Skeleton();
		skeleton.setTranslateX(WIDTH / 4);
		skeleton.setTranslateY(HEIGHT / 2);
		final Bone head = new Bone(20);
		head.setSkeleton(skeleton);
		head.getContent().add(
				new Circle(10, RadialGradientBuilder.create().centerX(0.25).centerY(0.25).radius(1)
						.stops(new Stop(0.0, Color.RED), new Stop(1.0, Color.DARKRED)).build()));
		Bone iterator = head;
		final Paint fill = RadialGradientBuilder.create().centerX(0.25).centerY(0.25).radius(1)
				.stops(new Stop(0.0, Color.LIGHTGREEN), new Stop(1.0, Color.GREEN)).build();
		for (int i = 0; i < length; i++)
		{
			final Bone bone = new Bone(20, -60, 60);
			bone.getContent().add(new Circle(10, fill));
			iterator.getChildren().add(bone);
			iterator = bone;
		}
		return skeleton;
	}

	public static void main(String... args)
	{
		launch(args);
	}
}

Symptom ist, dass es nur einmal gelingt sich zu verkeinern (Zoom out) und das mehr als erwartet. Danach lässt es sich aber nicht mehr vergrößern (Zoom in).
Irgendwie ist die notwendige Logik noch falsch, die ich versuche anzuwenden. Ich forsche also noch weiter.
Falls jemand eine Idee hat was ich hier falsch mache, freue ich mch auf Gedankenaustausch

Takidoso
 

vanilla

Mitglied
Das sieht gruselich aus:
Code:
if (m_mouseMiddleButtonDown)
{
    root.setScaleX(root.getScaleX() + scrollEvent.getDeltaY() > 0 ? +.1 : -.1);
    root.setScaleY(root.getScaleY() + scrollEvent.getDeltaY() > 0 ? +.1 : -.1);
}

Probiers mal so:
Code:
if (m_mouseMiddleButtonDown)
{
    root.setScaleX(root.getScaleX() + (scrollEvent.getDeltaY() > 0 ? +.1 : -.1));
    root.setScaleY(root.getScaleY() + (scrollEvent.getDeltaY() > 0 ? +.1 : -.1));
}
 

takidoso

Erfahrenes Mitglied
Hi Vanilla,
ja, Mensch .... Du hast recht. Kaum nach verwendung der Klamern funktioniert es auch zuverlässig.
Tausend Dank, das hatte ich glatt nicht wahrgenommen, dass das hier einen Unterschied macht.

Takidoso