Jak sterować programem JavaFX za pomocą klawiatury?

0

Pytanie tak jak w temacie.
W jaki sposób w javafx można nasłuchiwać zdarzenia klawiatury.
Poprzednio w java i java Swing używałem takiej klasy:


package klawiatura;

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;

public class Klawiatura {

    public static void main(String[] args) {
        new Keys().start();
    }
}

class Keys extends Thread {

    private volatile boolean quit;
    private WinUser.HHOOK hhk;
    private WinUser.LowLevelKeyboardProc keyboardHook;

    @Override
    public void run() {
        final User32 lib = User32.INSTANCE;
        WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
        keyboardHook = new WinUser.LowLevelKeyboardProc() {
            @Override
            public WinDef.LRESULT callback(int nCode, WinDef.WPARAM wParam, WinUser.KBDLLHOOKSTRUCT info) {
                if (nCode >= 0) {
                    switch (wParam.intValue()) {
                        case WinUser.WM_KEYUP:
                        case WinUser.WM_SYSKEYUP:
//                            System.out.println("in callback, key=" + info.vkCode);
                            if (info.vkCode == 27/*Esc+*/) {
                                quit = true;
                            } else if (info.vkCode == 65/*[A]*/) {
                                System.out.println("Nacisnieto Przycisk:  [ A ]");
                            } else if (info.vkCode == 66/*[B]*/) {
                                System.out.println("Nacisnieto Przycisk:  [ B ]");
                            } else if (info.vkCode == 67/*[C]*/) {
                                System.out.println("Nacisnieto Przycisk:  [ C ]");
                            } else if (info.vkCode == 112/*[F1]*/) {
                                System.out.println("Nacisnieto Przycisk:  [ F1 ]");
                            }
                    }
                }
                return lib.CallNextHookEx(hhk, nCode, wParam, info.getPointer());
            }
        };
        hhk = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHook, hMod, 0);
        System.out.println("Keyboard hook installed, type anywhere, 'q' to quit");
        new Thread() {
            @Override
            public void run() {
                while (!quit) {
                    try {
                        Thread.sleep(30);

                    } catch (InterruptedException ex) {
                        Logger.getLogger(Keys.class.getName()).log(Level.SEVERE, null, ex);
                    }

                }
                System.err.println("unhook and exit");
                lib.UnhookWindowsHookEx(hhk);
                Platform.exit();
                System.exit(0);
            }
        }.start();
//         This bit de returns from GetMessage
        int result = 0;
        WinUser.MSG msg = new WinUser.MSG();
        while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) {
            if (result == -1) {
                System.err.println("error in get message");
                break;
            } else {
                System.err.println("got message");
                lib.TranslateMessage(msg);
                lib.DispatchMessage(msg);
            }
        }
        lib.UnhookWindowsHookEx(hhk);
    }
}


 

natomiast w JavaFX przy próbie wyświetlenia tekstu w TextArea wywala błąd:

 Exception in thread "Thread-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
	at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
	at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
	at javafx.scene.Scene.addToDirtyList(Scene.java:485)
	at javafx.scene.Node.addToSceneDirtyList(Node.java:424)
	at javafx.scene.Node.impl_markDirty(Node.java:415)
	at javafx.scene.shape.Shape.impl_markDirty(Shape.java:942)
	at javafx.scene.Node$6.invalidated(Node.java:1224)
	at javafx.beans.property.DoublePropertyBase.markInvalid(DoublePropertyBase.java:112)
	at javafx.beans.property.DoublePropertyBase.access$000(DoublePropertyBase.java:52)
	at javafx.beans.property.DoublePropertyBase$Listener.invalidated(DoublePropertyBase.java:262)
	at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
	at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
	at javafx.beans.binding.DoubleBinding.invalidate(DoubleBinding.java:222)
	at com.sun.javafx.binding.BindingHelperObserver.invalidated(BindingHelperObserver.java:51)
	at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
	at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
	at javafx.beans.binding.BooleanBinding.invalidate(BooleanBinding.java:176)
	at com.sun.javafx.binding.BindingHelperObserver.invalidated(BindingHelperObserver.java:51)
	at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:349)
	at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
	at javafx.beans.property.ReadOnlyIntegerWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:176)
	at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:142)
	at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:113)
	at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:146)
	at javafx.scene.control.TextInputControl.selectRange(TextInputControl.java:899)
	at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:470)
	at javafx.scene.control.TextInputControl.insertText(TextInputControl.java:397)
	at javafx.scene.control.TextInputControl.appendText(TextInputControl.java:387)
	at Keyboard.Keys$1.callback(Keys.java:53)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback(CallbackReference.java:293)
	at com.sun.jna.CallbackReference$DefaultCallbackProxy.callback(CallbackReference.java:315)
	at com.sun.jna.Function.invokeInt(Native Method)
	at com.sun.jna.Function.invoke(Function.java:299)
	at com.sun.jna.Function.invoke(Function.java:224)
	at com.sun.jna.Library$Handler.invoke(Library.java:212)
	at com.sun.proxy.$Proxy0.GetMessage(Unknown Source)
	at Keyboard.Keys.run(Keys.java:104)

Czy może ktoś mi powiedzieć jak się to robi w javafx?

1

Błąd Ci wywala, bo nieumiejętnie korzystasz z wątków. Concurrency in JavaFX
Natomiast w obsłudze tej biblioteki musi pomóc Ci ktoś inny ;)

0

Wywołanie metod nie związanych z javafx działa:

np. System.out.println(""); lub ESC zamyka program

błąd pojawia się jeśli próbuje zainicjować komunikacje z metodami javafx takimi jak umieszczenie textu w TextArea itp.

0

Nie jestem pewien czy do końca zrozumiałem o co pytasz, ale czy chodzi ci o coś takiego?

package forum02;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Forum02 extends Application{

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

    @Override
    public void start(Stage primaryStage){
        VBox vbox = new VBox(5);
        vbox.setAlignment(Pos.CENTER);
        Button btn = new Button("Click me!");
        TextArea textarea1 = new TextArea();
        TextArea textarea2 = new TextArea();
        textarea2.setEditable(false);
        vbox.getChildren().addAll(textarea1, btn, textarea2);
        
        btn.setOnAction(e -> {
            String text;
            text = textarea1.getText();
            textarea2.setText(text);
        });
        
        Scene scene = new Scene(vbox, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Wpisujemy tekst do jednego textarea1 i po naciśnięciu przycisku napis zostaje przekazany do textarea2 i tam wyświetlony.
Oczywiście można w podobny sposób inicjować wyświetlanie dowolnych napisów czy też definiować inne działania.

//////EDIT:

Jeszcze zobacz coś takiego:

package forum02;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Forum02 extends Application{

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

    @Override
    public void start(Stage primaryStage){
        VBox vbox = new VBox(10);
        vbox.setAlignment(Pos.CENTER);
        TextArea textarea = new TextArea();
        textarea.setEditable(false);
        vbox.getChildren().addAll(textarea);
        textarea.setOnKeyPressed((KeyEvent event) -> {
            textarea.setText(event.getText());
        });
        
        Scene scene = new Scene(vbox, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}
0

Chodzi o coś innego.

Mianowicie chodzi o to aby program nasłuchiwał zdarzenia klawiatury w Windows nawet gdy program nie jest aktywny, na przykład mój program jest uruchomiony ale okno nie jest aktywne bo obecnie pracuje w przeglądarce internetowej i po naciśnięciu określonego przycisku na klawiaturze (może to być PrtScreen, Enter lub [9], ustalony wcześniej przycisk), program zrobi zrzut ekranu, nada odpowiednią nazwę pliku i zapisze do określonego katalogu oraz wyświetli tekst w TextArea.

1 użytkowników online, w tym zalogowanych: 0, gości: 1