Update TextFlow from another class JavaFx - java

I'm here with my JavaFx app.
I have two class:
I start these class in two different Thread. Because server is blockable.
My UI class ( Called Main ( I know i need to change this)).
A Server class ( Called Serveur )).
In my Server Class when i receive bytes with a ServerSocket.
I need to update a TextFlow (called flowMessafe) in my UI class :
with flowMessage.getChildren().add(); I can update this TextFlow without problem in my UI class.
But i my Server class i can't.
EDIT : I try all the things, but i think i found a big problem. I update the wrong instance of my javafx app. I change the code with current code
He is a part of my server code
My Server Constructor
public Serveur(Mediator med){
this.med=med;
try {
lancer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Loop to catch message or file.
for(;;){
try {
Thread.sleep(1L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Attente de communication ...");
try {
socket = serverSocket.accept();
} catch (IOException ex) {
System.out.println("Can't accept client connection. ");
}
try {
in = socket.getInputStream();
} catch (IOException ex) {
System.out.println("Can't get socket input stream. ");
}
byte[] bytes = new byte[16*1024];
while ((count = in.read(bytes)) > 0) {}
String mode = new String(bytes);
String[] split = mode.split(";");
if(split[0].compareTo("Message")==0){
recevoirMessage();
} else if(split[0].compareTo("Fichier")==0){
recevoirFichier(split[2]);
}
in.close();
socket.close();
}
When i receive a message i go to this function :
public void recevoirMessage() {
output = new ByteArrayOutputStream();
try {
socket = serverSocket.accept();
} catch (IOException ex) {
System.out.println("Can't accept client connection. ");
}
try {
in = socket.getInputStream();
} catch (IOException ex) {
System.out.println("Can't get socket input stream. ");
}
byte[] bytes = new byte[16*1024];
try {
while ((count = in.read(bytes)) > 0) {}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("Message reçus");
String recu = "Message : "+new String(bytes);
System.out.println(recu);
Platform.runLater(() -> {
Label mes = new Label(new String(bytes));
med.cli.flowMessage.getChildren().add(mes);
});
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
And in my main i have only empty constructor like
public Main(){}
In my UI class i have this to create my App :
'#Override
public void start(Stage primaryStage) {
try {
this.pStage = primaryStage;
this.pStage = new Stage();
idPane = new BorderPane();
Parent page;
page = FXMLLoader.load(getClass().getResource("/application/application.fxml"));
this.pStage.setTitle("Messagerie");
Scene scene = new Scene(page);
flowMessage = new TextFlow();
idContenuMessage= new ChoiceBox<String>();
scrollPane= new ScrollPane();
//flowMessage = new TextFlow();
String css = this.getClass().getResource("application.css").
toExternalForm();
scene.getStylesheets().clear();
scene.getStylesheets().add(css);
this.pStage.setScene(scene);
this.pStage.setResizable(false);
this.pStage.show();
this.pStage.setOnCloseRequest(event -> {
Serveur.close();
});
} catch (IOException e) {
e.printStackTrace();
}
}'
And i don't know how to update my UI TextFlow in my server Class.
I saw different things like the Mediator Pattern, i try this but it didn't work ( maybe i do something wrong ).
I start my app with this class :
package communication;
import application.Main;
import javafx.application.Application;
import javafx.stage.Stage;
public class Mediator extends Application implements Runnable {
private Serveur serv;
public Main cli;
public Thread thread;
private Stage primaryStage;
public static void main(String args[]){
launch(args);
}
public Mediator(){
cli = new Main();
thread = new Thread(this,"serv");
thread.start();
}
#Override
public void run() {
setServ(new Serveur(this));
}
#Override
public void start(Stage stage) throws Exception {
primaryStage = stage;
cli.start(primaryStage);
}
public Serveur getServ() {
return serv;
}
public void setServ(Serveur serv) {
this.serv = serv;
}
}
Thanks for Helping.

I would use Platform.runLater() to put your GUI changes on the Javafx application thread, e.g.
public void recevoirMessage() {
output = new ByteArrayOutputStream();
try {
socket = serverSocket.accept();
} catch (IOException ex) {
System.out.println("Can't accept client connection. ");
}
try {
in = socket.getInputStream();
} catch (IOException ex) {
System.out.println("Can't get socket input stream. ");
}
byte[] bytes = new byte[16*1024];
try {
while ((count = in.read(bytes)) > 0) {}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("Message reçus");
String recu = "Message : "+new String(bytes);
System.out.println(recu);
//inserted here
Platform.runLater(new Runnable() {
#Override public void run() {
//HERE I WANT TO UPDATE MY TEXTFLOW
}
});
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Firstly TextFlow does not have text property so you can't set that value to it. Add Text object to text flow:
Text text = new Text();
flowMessage = new TextFlow(text);
Then create StringProperty, bind it to Text component textProperty and update this value in Server class.
Application class:
public class Mediator extends Application implements Runnable {
private Serveur serv;
public Main cli;
private StringProperty textProperty = new SimpleStringProperty("text");
public Thread thread;
private Stage primaryStage;
public static void main(String args[]){
launch(args);
}
public Mediator(){
cli = new Main(this,textProperty);
thread = new Thread(this,"serv");
thread.start();
}
#Override
public void run() {
serv = new Serveur(this,textProperty);
}
#Override
public void start(Stage stage) throws Exception {
primaryStage = stage;
cli.start(primaryStage);
}
}
Pass textProperty to Main and Serveur classes.
cli = new Main(this, textProperty);
serv = new Serveur(this, textProperty);
Bind text property to Text component:
text.textProperty().bind(textProperty);
Finally update textProperty in JavaFX Application Thread in your Serveur class:
public void recevoirMessage() {
output = new ByteArrayOutputStream();
try {
socket = serverSocket.accept();
} catch (IOException ex) {
System.out.println("Can't accept client connection. ");
}
try {
in = socket.getInputStream();
} catch (IOException ex) {
System.out.println("Can't get socket input stream. ");
}
byte[] bytes = new byte[16*1024];
try {
while ((count = in.read(bytes)) > 0) {}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("Message reçus");
String recu = "Message : "+new String(bytes);
System.out.println(recu);
//inserted here
Platform.runLater(new Runnable() {
#Override public void run() {
//HERE I WANT TO UPDATE MY TEXTFLOW
textProperty.setValue(new String(bytes));
}
});
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Related

Why can't I use setText() under Button event?

So I am remaking my kinda complex console program to GUI. However, I am very unexperienced JavaFX user.
Label cityNameLabel = (Label) scene.lookup("#cityNameLabel");
cityNameLabel.setText("No text");
Button startButton = (Button) scene.lookup("#startButton");
Here I have my Label successfully initiated. At this moment I can still use cityNameLabel.setText(); and it is gonna work.
This continues:
startButton.setOnAction(e -> {
String enteredUrl = linkField.getText();
if(isValidUrl(enteredUrl)) {
try {
cityNameLabel.setText("Test");
doJob(enteredUrl, cityNameLabel);
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}.........
When I try to change the label here it won't happen. What I actually need to do is to reference the cityNamelLabel to doJob() method and I want to doJob() method to change it (once it finds the name of the city).
Can anyone give a me a solution that would allow me to do what I want to do? (changing it afterwards from doJob();). Thank you!
EDIT: Here I post my attemp of minimal version. I hope it is enough
package net.maty;
public class Katastr extends Application {
final String programName = "KatastRED";
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage mainStage) throws Exception {
try {
mainStage.setTitle(programName);
mainStage.setResizable(false);
BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Katastr.fxml"));
Scene scene = new Scene(root,800,500);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
ChoiceBox threadNumberBox = (ChoiceBox) scene.lookup("#threadNumberBox");
threadNumberBox.getSelectionModel().select(5);
TextField linkField = (TextField) scene.lookup("#linkField");
//debug
linkField.setText("https://regiony.kurzy.cz/katastr/stary-kolin/objekty?strana=");
//
Text cityNameText = (Text) scene.lookup("#cityNameText");
Button startButton = (Button) scene.lookup("#startButton");
//HERE IS THE THING. EVERYTHING WORKS WELL EXCEPT THE FACT I CANT CHANGE CITYNAMETEXT
startButton.setOnAction(e -> {
String enteredUrl = linkField.getText();
if(isValidUrl(enteredUrl)) {
try {
cityNameText.setText("Example that will not show because it doesnt work.");
doJob(enteredUrl, cityNameText);
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
else {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Chyba!");
alert.setHeaderText(null);
alert.setContentText("Chybně zadané URL!");
alert.showAndWait();
}
});
mainStage.setScene(scene);
mainStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void doJob(String validatedUrl, Text cityNameText) throws IOException, InterruptedException {
Scanner in = new Scanner(System.in);
String url = validatedUrl;
String cityName = getCityName(url);
cityNameText.setText(cityName);
List<String> parcelLinks = getParcelLinks(url);.....
}
public static List<String> getParcelLinks(String url) throws IOException{
}
public static List<String> getAdressesFromPage(Document doc) {
}
public static List<String> createParcelUrls(List<String> links) throws IOException {
}
public static boolean isValidUrl(String url) {
}
public static String getCityName(String url) throws IOException {
}
}

Server-GUI freezes when waiting for connections

I'm trying to create a multithreaded server, connecting to it through a client works fine, but the GUI is frozen. Probably since it is constantly listening for incoming connections it is stuck in a while loop. So, do i need to create a thread for the multithreaded server (which itself will be creating threads) or how would i go about fixing this?
MultiThreadedServer:
public class MultiThreadServer{
public static final int PORT = 2000;
//function below is called by the GUI.
public void startServer() throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println("Server socket: " + s);
System.out.println("Server listening...");
try { while(true) {
// Blocks until a connection occurs:
Socket socket = s.accept();
System.out.println("Connection accepted.");
System.out.println("The new socket: " + socket);
try {
ClientThread t = new ClientThread(socket);
System.out.println("New thread started.");
System.out.println("The new thread: " + t);
}
catch(IOException e) {
System.out.println("===Socket Closed===");
socket.close();
}
}
}
finally { System.out.println("===ServerSocket Closed==="); s.close(); }
}
}
}
ServerGUI:
public class ServerGUI extends Application implements Initializable{
#FXML private MenuItem startConn;
#Override
public void start(Stage stage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("ServerGUI.fxml"));
stage.setScene(new Scene(root));
stage.show();
} catch (IOException ex) {
ex.printStackTrace();
System.exit(0);
}
}
public static void main(String[] args) {
launch(args);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
startConn.setOnAction(e -> {
try {
MultiThreadServer mts = new MultiThreadServer();
mts.startServer();
System.out.println("Starting Server...");
} catch (IOException e1) {
e1.printStackTrace();
}
});
}
}
As i suspected, a new thread for the server was required.
MultiThreadServer:
public Runnable startServer() throws IOException {
After making the function Runnable i added this:
ServerGUI:
startConn.setOnAction(e -> {
Thread t1 = new Thread(new Runnable() {
public void run()
{
MultiThreadServer mts = new MultiThreadServer();
try {
mts.startServer();
} catch (IOException e) {
e.printStackTrace();
}
}});
t1.start();
Thanks to user BackSlash for providing reference-link.

Outputstream write() gets blocked using RXTX to communicate with HC-05 BT Module

I've tried to solve the following Problem the last three days. I spend three days reading through various threads in many forums.
This is my problem:
I try to send commands to a Bluetooth HC-05 device connected via a virtual Com-Port. In my case "COM4".
I used RXTX to establish a Connection to the Serial port. This works fine.
public static void main(String[] args) {
portList = CommPortIdentifier.getPortIdentifiers();
boolean portFound = false;
while (portList.hasMoreElements()) {
portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
if (portId.getName().equals("COM4")) {
portFound = true;
SimpleRead reader = new SimpleRead();
}
}
}
}
#SuppressWarnings("restriction")
public SimpleRead() {
try {
serialPort = (SerialPort) portId.open("SimpleReadApp", 2000);
} catch (PortInUseException e) {System.out.println(e);}
try {
serialPort.setOutputBufferSize(15);
serialPort.setEndOfInputChar((byte)'\n');
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
} catch (IOException e) {System.out.println(e);} catch (UnsupportedCommOperationException e)
e.printStackTrace();
}
try {
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {System.out.println(e);}
serialPort.notifyOnDataAvailable(true);
try {
serialPort.setSerialPortParams(300,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
} catch (UnsupportedCommOperationException e) {System.out.println(e);}
serialPort.setRTS(true);
serialPort.setDTR(true);
serialPort.notifyOnDataAvailable(true);
serialPort.setInputBufferSize(8192);
serialPort.setOutputBufferSize(8192);
writeThread = new Thread(new SerialWriter(outputStream));
writeThread.start();
readThread = new Thread(this);
readThread.start();
}
The Problem occures in the writer thread. When the writeTo() method of the ByteArrayOutputStream gets called the program hangs up without throwing an exception.
public class SerialWriter implements Runnable {
OutputStream out;
public SerialWriter(OutputStream out) {
this.out = out;
}
public void run() {
try {
Thread.sleep(500);
while (true) {
out.flush();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("AT\n".getBytes(Charset.forName("ASCII")));
baos.writeTo(out); // <-- Here is my Problem. The program just
// hangs up
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
My OS is Windows 8.1 64bit.
I'm able to connect to the Bluetooth device with the console application hterm.
I appreciate any help or tip you can give me to solve this annoying problem!!

How do I avoid a null pointer exception when working with serial ports in java?

So, I'm making a program that communicates with a sensor by sending commands. However, when I try to click on the buttons within my GUI, it comes up with two errors a nullpointer exception, and a portinuse exception, which prevent the button from working. How do I fix this?
SimpleRead class:
import java.awt.event.ActionListener;
import java.io.*;
import java.util.*;
import javax.comm.*;
import javax.swing.*;
public class SimpleRead {
static Enumeration portList;
static CommPortIdentifier portId;
static PrintStream os;
static BufferedReader is;
static java.util.Timer t = new java.util.Timer();
static TimersTask tt = new TimersTask();
public static void main(String[] args) {
GUI GUI = new GUI();
JFrame window = new JFrame("DI-100");
window.setSize(700, 300);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(GUI);
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
String wantedPortName;
CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
ArrayList<String> serialports = new ArrayList<String>();
serialports.add(portId.getName());
String[] ports = new String[serialports.size()];
ports = serialports.toArray(ports);
GUI.jComboBox2 = new JComboBox(ports);
GUI.jComboBox2.addActionListener(GUI.jComboBox2);
wantedPortName = (String) GUI.jComboBox2.getSelectedItem();
while (portList.hasMoreElements()) {
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL &&
portId.getName().equals(wantedPortName)) {
CommPortIdentifier pid = portId;
SerialPort port = null;
try {
port = (SerialPort) portId.open("OpenPort", 1000);
} catch(PortInUseException e) {
System.err.println("Port already in use: " + e);
}
try {
port.setSerialPortParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
}
catch (UnsupportedCommOperationException e) {
System.out.println("you suck");
}
try {
os = new PrintStream(port.getOutputStream(), true, "ISO-8859-1");
}
catch (IOException e) {
System.err.println("Can't open input stream: write-only");
}
try {
is = new BufferedReader(new InputStreamReader(port.getInputStream()));
} catch (IOException e) {
System.err.println("Can't open input stream: write-only");
}
try{
os = new PrintStream(port.getOutputStream(), true);
} catch (IOException e){
}
}
else {
}
}
}
}
}
TimersTask class:
import java.io.IOException;
import java.util.*;
public class TimersTask extends TimerTask{
#Override
public void run() {
SimpleRead.os.print("W");
try {
String returnvalue = SimpleRead.is.readLine();
System.out.println(returnvalue);
GUI.jTextField1.setText(returnvalue);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
//is.readLine(); // Second read will remove the extra line feed that AT generates as
} catch (IOException e){
}
}
}
OK so here's what I think is happening. You're trying to open the port: port = (SerialPort) portId.open("OpenPort", 1000);, and that fails for whatever reason. You then go on to use it again: port.setSerialPortParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);, except that it's NULL at this point, so your program explodes.
SimpleRead class:
import java.awt.event.ActionListener;
import java.io.*;
import java.util.*;
import javax.comm.*;
import javax.swing.*;
public class SimpleRead {
static Enumeration portList;
static CommPortIdentifier portId;
static PrintStream os;
static BufferedReader is;
static java.util.Timer t = new java.util.Timer();
static TimersTask tt = new TimersTask();
public static void main(String[] args) {
GUI GUI = new GUI();
JFrame window = new JFrame("DI-100");
window.setSize(700, 300);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(GUI);
Enumeration portList = CommPortIdentifier.getPortIdentifiers();
String wantedPortName;
CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
ArrayList<String> serialports = new ArrayList<String>();
serialports.add(portId.getName());
String[] ports = new String[serialports.size()];
ports = serialports.toArray(ports);
GUI.jComboBox2 = new JComboBox(ports);
GUI.jComboBox2.addActionListener(GUI.jComboBox2);
wantedPortName = (String) GUI.jComboBox2.getSelectedItem();
while (portList.hasMoreElements()) {
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL &&
portId.getName().equals(wantedPortName)) {
CommPortIdentifier pid = portId;
SerialPort port = null;
try {
port = (SerialPort) portId.open("OpenPort", 1000);
port.setSerialPortParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
os = new PrintStream(port.getOutputStream(), true, "ISO-8859-1");
is = new BufferedReader(new InputStreamReader(port.getInputStream()));
os = new PrintStream(port.getOutputStream(), true); // you had this line in your original code. Is this really necessary, since you have a similar line 2 lines before it?
} catch(PortInUseException e) {
System.err.println("Port already in use: " + e);
} catch (UnsupportedCommOperationException e) {
System.out.println("you suck");
} catch (IOException e) {
System.err.println("Can't open input or output stream");
}
}
else {
}
}
}
try {
os.close();
} catch (Exception ex) {
//already closed
}
try {
is.close();
} catch (Exception ex) {
//already closed
}
// close the port here
}
}
TimersTask class:
import java.io.IOException;
import java.util.*;
public class TimersTask extends TimerTask{
#Override
public void run() {
SimpleRead.os.print("W");
try {
String returnvalue = SimpleRead.is.readLine();
System.out.println(returnvalue);
GUI.jTextField1.setText(returnvalue);
Thread.sleep(1000);
//is.readLine(); // Second read will remove the extra line feed that AT generates as
} catch (IOException e){
} catch (InterruptedException e) {
}
}
}
This fixes one of your problems, and a whole lot of issues you didn't know you had. the way a try/catch is designed to work is so when something fails, the rest of the code inside the block does not run, because it is dependent on what failed. Your code, instead, would simply run through all that throwing exception after exception as it failed.
The underlying problem is the PortInUseException, which is self-explanatory. Something else is already using the port. Check your code (here and elsewhere) to make sure you aren't trying to open the port twice (like, if you click the button more than once perhaps?), and if that doesn't solve the issue, there may be another program on your computer grabbing the port.
"Remember kids, always close your ports when you're done with them!"

Thread.join does not seem to work in my code; am I using it right?

I have a class Automator that can automate a user. I am specifically having problems setting the system clipboard in windows. The Automator class makes use of the ClipSetThread class, which is a thread that sets the system clipboard. A instance of ClipSetThread takes as input a thread, that if null, it joins with (waits for it to complete).
I feel that I am not calling ClipSetThread right because I still have the errors I have had before in its reliability; prior to the ClipSetThread. This code does not throw any errors when it runs, it works about 2/3 of the time though. Other times it will print 1134, _234, or etc. It seems that the threads are not joining (waiting for) each other, or get skipped.
Code:
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputListener;
public class Automator extends Thread implements NativeMouseInputListener
{
Robot rob = null;
TheAppClass theApp = null;
ClipSetThread lastClipSet = null;
boolean doit = false;
boolean settingClip = false;
public void run()
{
try // to make the Global hook
{
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex){theApp.updateOutput("No Global Keyboard or Mouse Hook");return;}
try // to create a robot (can simulate user input such as mouse and keyboard input)
{
rob = new Robot();
}
catch (AWTException e1) {theApp.updateOutput("The Robot could not be created");return;}
while(true) {}
}
public void setApp(TheAppClass app)
{
theApp = app;
theApp.updateOutput("Succesfully started automator");
}
public void setClip(String arg)
{
ClipSetThread set = new ClipSetThread(theApp, lastClipSet);
lastClipSet = set;
set.setClip(arg);
}
public void DOit()
{
theApp.updateOutput("Starting");
pasteAtCursorLocation("1");
tab(1);
pasteAtCursorLocation("2");
tab(1);
pasteAtCursorLocation("3");
tab(1);
pasteAtCursorLocation("4");
tab(1);
theApp.updateOutput("Complete");
}
public void nativeMouseReleased(NativeMouseEvent e)
{
//System.out.println("Mouse Released: " + e.getButton());
if(doit)
{
DOit();
doit = false;
}
}
public void pasteAtCursorLocation(String text)
{
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
}
public void tab(int numTimes)
{
while(numTimes > 0)
{
rob.keyPress(KeyEvent.VK_TAB);
rob.keyRelease(KeyEvent.VK_TAB);
numTimes--;
theApp.updateOutput("Simulated Tab");
}
}
// Unimplemented
public void nativeMouseClicked(NativeMouseEvent arg0) {}
public void nativeMousePressed(NativeMouseEvent arg0) {}
public void nativeMouseDragged(NativeMouseEvent arg0) {}
public void nativeMouseMoved(NativeMouseEvent arg0) {}
}
ClipSetThread:
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
public class ClipSetThread extends Thread
{
Clipboard sysClip = null;
TheAppClass theApp = null;
public ClipSetThread(TheAppClass app, Thread waitFor)
{
theApp = app;
sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
if(waitFor != null)
{try {waitFor.join();}catch (InterruptedException e) {}}
}
public void setClip(String arg)
{
// Two strings that will hopefully never be on the clipboard
String checkStr1 = "9999999999999";
String checkStr2 = "99999999999999";
// When we read in the clipboard we want to see if we change these strings from the ones they
// will never be, if they do change we read the clipboard successfully
String clipBoardTextBefore = checkStr1;
String clipBoardTextAfter = checkStr2;
// First get a copy of the current system clipboard text
while(true)
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
// If we failed to change the string it means we failed to read the text
if(clipBoardTextBefore.equals(checkStr1))
theApp.updateOutput("Could NOT get sysClip text");
else
{
// If we didn't failed to get the current text try to change it
while(true)
{
try{sysClip.setContents(new StringSelection(arg), null);}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
// Now again check to see the clipboard text
while(true)
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextAfter = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
// If we failed to read the clipboard text
if(clipBoardTextAfter.equals(checkStr2))
theApp.updateOutput("Could NOT check if sysClip update was successful");
else
{ // We re-read the clipboard text, see if it changed from the original clipboard text
if(clipBoardTextAfter.equals(checkStr1))
theApp.updateOutput("Could NOT successfully set clipboard text");
else
theApp.updateOutput("Set Clipboard Text:" + arg + "\n");
}
}
}
}
So, firstly, you never call start on the ClipSetThread. You should also check to see if the thread is still alive before joining it.
public class ClipSetThread extends Thread {
Clipboard sysClip = null;
TheAppClass theApp = null;
private String toClipboard;
public ClipSetThread(TheAppClass app, Thread waitFor, String toClipBoard) {
theApp = app;
sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
this.toClipboard = toClipBoard;
// !! Check to see if the thread is also alive before trying to join with it...
if (waitFor != null && waitFor.isAlive()) {
try {
waitFor.join();
} catch (InterruptedException e) {
}
}
}
// You should really put your logic into the `run` method in order to allow
// the code to actually run in a separate thread...otherwise there is no
// point in using a thread....
#Override
public void run() {
// Two strings that will hopefully never be on the clipboard
String checkStr1 = "9999999999999";
String checkStr2 = "99999999999999";
// When we read in the clipboard we want to see if we change these strings from the ones they
// will never be, if they do change we read the clipboard successfully
String clipBoardTextBefore = checkStr1;
String clipBoardTextAfter = checkStr2;
// First get a copy of the current system clipboard text
while (true) {
try {
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String) contents.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
}
continue;
}
break;
}
// If we failed to change the string it means we failed to read the text
if (clipBoardTextBefore.equals(checkStr1)) {
theApp.updateOutput("Could NOT get sysClip text");
} else {
// If we didn't failed to get the current text try to change it
while (true) {
try {
sysClip.setContents(new StringSelection(toClipboard), null);
} catch (Exception e) {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
}
continue;
}
break;
}
// Now again check to see the clipboard text
while (true) {
try {
Transferable contents = sysClip.getContents(null);
clipBoardTextAfter = (String) contents.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
}
continue;
}
break;
}
// If we failed to read the clipboard text
if (clipBoardTextAfter.equals(checkStr2)) {
theApp.updateOutput("Could NOT check if sysClip update was successful");
} else { // We re-read the clipboard text, see if it changed from the original clipboard text
if (clipBoardTextAfter.equals(checkStr1)) {
theApp.updateOutput("Could NOT successfully set clipboard text");
} else {
theApp.updateOutput("Set Clipboard Text:" + toClipboard + "\n");
}
}
}
}
}
As per our previous converstaion, it's dangerous to use while (true) {}, it's also wasteful, as it will consume CPU cycles unnecessarily...
public class Automator extends Thread implements NativeMouseInputListener {
// A "locking" object...
private static final Object WAIT_LOCK = new Object();
Robot rob = null;
TheAppClass theApp = null;
ClipSetThread lastClipSet = null;
boolean doit = false;
boolean settingClip = false;
public void run() {
try // to make the Global hook
{
GlobalScreen.registerNativeHook();
} catch (NativeHookException ex) {
theApp.updateOutput("No Global Keyboard or Mouse Hook");
return;
}
try // to create a robot (can simulate user input such as mouse and keyboard input)
{
rob = new Robot();
} catch (AWTException e1) {
theApp.updateOutput("The Robot could not be created");
return;
}
// This is wasteful...
// while (true) {
// }
// Locks do not consume CPU cycles while in the wait state...
synchronized (WAIT_LOCK) {
try {
WAIT_LOCK.wait();
} catch (Exception exp) {
}
}
}
public void dispose() {
// Tell the thread it can terminate...
synchronized (WAIT_LOCK) {
WAIT_LOCK.notify();
}
// This will STOP the current thread (which called this method)
// while the lastClipSet finishes...
if (lastClipSet != null && lastClipSet.isAlive()) {
lastClipSet.join();
}
}
public void setClip(String arg) {
ClipSetThread set = new ClipSetThread(theApp, lastClipSet, arg);
lastClipSet = set;
// You MUST START the thread...
set.start();
}
/*...*/
}
Updated
This code could produce a infinite loop. What happens if the clipboard does not contain a String value??
while(true)
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
You tend to do this a lot. I might suggest that you provide some kind of "escape" mechanism to allow it to fail after a number of retries...
boolean successful = false;
int retries = 0;
while (!successful && retries < 20) {
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
successful = true;
}
catch(Exception e)
{
retries++;
try {Thread.sleep(20);} catch (InterruptedException e1) {}
}
}
Updated with working example
Okay, that was fun. I've put together a (simple) working example. You will want to open a text editor of some kind. When you run the program, you have 5 seconds to make it active ;)
The only basic change I've made is I set added a auto delay between events of 250 milliseconds (see rob.setAutoDelay(250).
Now, you could also place a delay between each key event as well, using Robot#delay, but that's up to you
public class Engine extends Thread {
private Robot rob = null;
private PasteThread lastClipSet = null;
public void setClip(String arg) {
if (lastClipSet != null && lastClipSet.isAlive()) {
try {
lastClipSet.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
PasteThread set = new PasteThread(arg);
lastClipSet = set;
lastClipSet.start();
}
public void pasteAtCursorLocation(String text) {
System.out.println("Paste " + text);
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
}
public Engine() throws AWTException {
rob = new Robot();
rob.setAutoDelay(250);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
}
pasteAtCursorLocation("This is a simple test, thanks for watching!");
}
public static void main(String[] args) {
try {
new Engine();
} catch (AWTException ex) {
Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, null, ex);
}
}
public class PasteThread extends Thread {
private String toPaste;
public PasteThread(String value) {
toPaste = value;
}
#Override
public void run() {
Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
System.out.println("Current clipboard contents = " + getClipboardContents(sysClip));
sysClip.setContents(new StringSelection(toPaste), null);
System.out.println("New clipboard contents = " + getClipboardContents(sysClip));
}
public String getClipboardContents(Clipboard clipboard) {
String value = null;
boolean successful = false;
int retries = 0;
while (!successful && retries < 20) {
Transferable contents = clipboard.getContents(null);
if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
value = (String) contents.getTransferData(DataFlavor.stringFlavor);
successful = true;
} catch (Exception exp) {
retries++;
exp.printStackTrace();
}
} else {
retries++;
}
}
System.out.println(successful + "/" + retries);
return value;
}
}
}
Could you please try to repeat the Paste action with a sleep 1 second in between
public void pasteAtCursorLocation(String text)
{
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
// put in a sleep 1 second here
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
}
It could be that pasting 2x is giving different results. The reason for this strange behavior could the way Windows manages the clipboard. If pasting 2x the clipboard is giving different result then you know that the root cause for this strange behavior is not to find in your code but how Java and Windows work together.

Categories