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

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 {
}
}

Related

How to transfer string between methods (Java)

Essentially, I'm trying to collect data for a string and use it in another method. Whatever I've tried hasn't seemed to work. Please check out my code and tell me what to do.
I've tried to declare the string as a public string, but since it is in a method I can't.
My goal is to transfer the string "application_1" to button3's setOnAction method.
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("myDesktop");
window.setOnCloseRequest(e -> closeProgram());
button = new Button("Setup MyDesktop");
button3 = new Button("Start Test Application");
button2 = new Button("Choose Wheel Applications");
button2.setOnAction(e -> {
JFileChooser jfc = new JFileChooser();
jfc.showDialog(null, "Please select a file.");
jfc.setVisible(true);
File filename = jfc.getSelectedFile();
String application_1 = filename.getName();
if (application_1.endsWith(".exe")) {
System.out.println("File successfully chosen!");
} else {
System.out.println("File is not an application!");
System.out.println("Please choose another file!");
System.out.println("Issue Alert Box here...");
}
if (application_1 == null) {
System.out.println("No file selected!");
}
});
button.setOnAction(e -> {
AlertBox.display("Alert", "Save file?");
});
button3.setOnAction(e -> {
Runtime runtime = Runtime.getRuntime();
try {
Process process = runtime.exec("name_of_file");
} catch (IOException e1) {
e1.printStackTrace();
}
});
I want the string to be able to be used by the code
button3.setOnAction(e -> {
// code
});
Should work:
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("myDesktop");
window.setOnCloseRequest(e -> closeProgram());
String application_1 = "";
button = new Button("Setup MyDesktop");
button3 = new Button("Start Test Application");
button2 = new Button("Choose Wheel Applications");
button2.setOnAction(e -> {
JFileChooser jfc = new JFileChooser();
jfc.showDialog(null, "Please select a file.");
jfc.setVisible(true);
File filename = jfc.getSelectedFile();
application_1 = filename.getName();
if (application_1.endsWith(".exe")) {
System.out.println("File successfully chosen!");
} else {
System.out.println("File is not an application!");
System.out.println("Please choose another file!");
System.out.println("Issue Alert Box here...");
}
if (application_1 == null) {
System.out.println("No file selected!");
}
});
button.setOnAction(e -> {
AlertBox.display("Alert", "Save file?");
});
button3.setOnAction(e -> {
Runtime runtime = Runtime.getRuntime();
System.out.println(application_1); //here you can use the variable
try {
Process process = runtime.exec("name_of_file");
} catch (IOException e1) {
e1.printStackTrace();
}
});
Since you don't have control over the actual event that's created when the button is hit you cannot pass it directly (well, maybe you could add a tag to the button or something similar - I never used javafx).
But nothing prevents you from introducing an object-level variable to hold your string(s).
class YourClass {
private String myString = "";
public void start(Stage primaryStage) throws Exception {
// ...
button2.setOnAction(e -> {
this.myString = "application_1";
});
// ...
button3.setOnAction(e -> {
System.out.println(this.myString);
});
}
}
Alternatively, if your variable is final or effectively final you can just use it in your anonymous function:
class YourClass {
public void start(Stage primaryStage) throws Exception {
// ...
final String myString = "application_1";
// ...
button3.setOnAction(e -> {
System.out.println(myString);
});
}
}

Update TextFlow from another class JavaFx

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();
}
}

Play a QuickTime video with JavaFX

I try to open a MOV video file with JavaFX:
Media media = new Media ("file:/tmp/file.MOV");
But it throws a MediaException: MEDIA_UNSUPPORTED.
However, if I simply change the file extension from .MOV to .MP4, it works perfectly, and the video is played with no error.
How can I force JavaFX to play my file without having to rename it?
Well that was a fun two hours. Here you go! Just make sure you have a zero byte mp4 file like in the example. It's pretty key in how it works.
public class TestFrame extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
File actualFile = new File("shelter.mov");
File emptyfile = new File("blank.mp4");
Media media = new Media(emptyfile.toURI().toString());
copyData(media, actualFile);
MediaPlayer mediaPlayer = null;
try {
mediaPlayer = new MediaPlayer(media);
} catch (Exception e) {
e.printStackTrace();
}
mediaPlayer.setAutoPlay(true);
MediaView mediaView = new MediaView(mediaPlayer);
root.getChildren().add(mediaView);
Scene scene = new Scene(root, 720, 480);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void copyData(Media media, File f) {
try {
Field locatorField = media.getClass().getDeclaredField("jfxLocator");
// Inside block credits:
// http://stackoverflow.com/questions/3301635/change-private-static-final-field-using-java-reflection
{
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(locatorField, locatorField.getModifiers() & ~Modifier.FINAL);
locatorField.setAccessible(true);
}
CustomLocator customLocator = new CustomLocator(f.toURI());
customLocator.init();
customLocator.hack("video/mp4", 100000, f.toURI());
locatorField.set(media, customLocator);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
static class CustomLocator extends Locator {
public CustomLocator(URI uri) throws URISyntaxException {
super(uri);
}
#Override
public void init() {
try {
super.init();
} catch (Exception e) {
}
}
public void hack(String type, long length, URI uri){
this.contentType = type;
this.contentLength = length;
this.uri = uri;
this.cacheMedia();
}
}
}
How it works: Normally the default Locator throws an exception about the content type and it all ends there. Replacing the Media's locator with a custom one then manually setting the contentType, length, and uri gets it to play without a peep.

Why do JButtons that open webpages only work the first time one is clicked and then get deactivated?

I have a set of JButtons, each of which opens a separate YouTube video webpage. When first running the program, I can click on any ONE button and get the video page. When I try to get another video page with a button click, it doesn't work - in fact, all the buttons are deactivated. This is the case whether or not I close the video webpage.
How can I keep all the buttons activated? Thanks in advance.
Here's the code for reference. The button links and tags are populated from a text file.
//import statements
public class VideoRecord extends JFrame {
private File videoRecordFile;
public VideoRecord() throws FileNotFoundException {
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new GridLayout(2,2));
setSize(new Dimension(500, 500));
videoRecordFile = new File("videorecord.txt");
getButtons();
pack();
}
public void getButtons() throws FileNotFoundException {
Scanner input = new Scanner(videoRecordFile);
while (input.hasNextLine()) {
Scanner lineInput = new Scanner(input.nextLine());
while (lineInput.hasNext()) {
final String urlString = lineInput.next();
String buttonText = lineInput.next();
JButton btn = new JButton(buttonText);
add(btn);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
URL videoURL = new URL(urlString);
URLConnection videoConnection = videoURL.openConnection();
videoConnection.connect();
openWebpage(videoURL);
}
catch (MalformedURLException mue) {}
catch (IOException ioe) {}
setEnabled(false);
}
});
}
}
}
public static void openWebpage(URI uri) {
Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
try {
desktop.browse(uri);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void openWebpage(URL url) {
try {
openWebpage(url.toURI());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws FileNotFoundException {
VideoRecord vr = new VideoRecord();
}
}
Take a second to look at you code...
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
URL videoURL = new URL(urlString);
URLConnection videoConnection = videoURL.openConnection();
videoConnection.connect();
openWebpage(videoURL);
}
catch (MalformedURLException mue) {}
catch (IOException ioe) {}
setEnabled(false);
}
});
When you click a button you call setEnabled(false);...
This has actually disable the frame, not the button that was clicked...
Try using ((JButton)e.getSource()).setEnabled(false) instead
Don't throw away you Exceptions blindly, they provide important and useful information that can help solve problems

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