Java SWT updating canvas from another thread - java

I'm developing a Java application that does basically:
Starts a server in a new thread. This server opens a socket connection and waits for messages.
Creates a Shell with a Canvas (in the main thread) and whenever server receives a message, I need to draw a line using the points contained in it.
I've already read some threads about this issue (on updating SWT from another thread) and I've tried to create a method called by my server whenever it receives a message:
public static void doPaint(final Canvas canvas, final Draw draw){
if (canvas != null && draw != null){
Display.getDefault().syncExec(new Runnable() {
#Override
public void run() {
canvas.addPaintListener(new PaintListener() {
#Override
public void paintControl(PaintEvent e) {
System.out.println("Printing line....");
e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_WHITE));
e.gc.setForeground(e.display.getSystemColor(SWT.COLOR_BLACK));
e.gc.setLineWidth(3);
e.gc.drawLine((int)draw.getStartX(),(int)draw.getEndX(),(int)draw.getStartY(),(int)draw.getEndY());
}
});
}
});
}
}
EDIT
This is the loop that waits for socket messages and triggers the actions:
while(isConnected()){
try{
System.out.println("Waiting for connection...");
Socket server = getServerSocket().accept();
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream(), Const.ENC_UTF8));
String inputLine;
while(((inputLine = in.readLine()) != null)){
processInput(inputLine, canvas);
}
}catch(IOException ioex){
System.out.println(ioex.getMessage());
ioex.printStackTrace();
}catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
and my processInput method is:
protected void processInput(String inputLine, Canvas canvas){
//get draw from input stream
Draw draw = DrawUtil.getDrawFromInput(inputLine);
//draw points on live-mode
BBCanvas.doPaint(canvas, draw);
//append draw node to XML file
DrawUtil.writeDrawToFile(draw);
}
... but the runnable in the doPaint method is never executed. Any hints?
EDIT 2
This is the code that creates the main shell:
public MainWindow(BBServer server) {
this.server = server;
Display display = new Display();
this.mainShell = createShell(display);
getMainShell().open();
while (!getMainShell().isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}

Related

Java Multiple threads using a client socket

I'm creating a server based chat program that has multiple worker threads each handling a client on a socket. It has a single server socket that passes off the client to the worker thread.
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4001);
System.out.println("Listening server");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("Connected");
Random rand= new Random();
int port=4001+rand.nextInt(5);
Worker worker = new Worker(port);
executor.execute(worker);
System.out.println("Thread started");
new PrintWriter(socket.getOutputStream(), true).println(port);
socket.close();
System.out.println("Closed");
// break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public class Worker implements Runnable {
private int port;
public Worker(int i) {
port=i;
}
#Override
public void run() {
worker();
}
private static Socket socket;
private static PrintWriter out;
private static BufferedReader in;
private void worker() {
try {
ServerSocket serverSocket = new ServerSocket(port);
socket = serverSocket.accept();
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println("Received: " + line);
switch (line) {
case ("Button Press"):
System.out.println("Handled button");
out.println("Button acknowledged");
break;
case ("Give me some data"):
System.out.println("Handled data");
out.println("Have some data");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
out.close();
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This works fine, however the issue is when I have an automated request by the client to check for messages and the user provides some input at the same type. This causes conflict as the actual methods take a couple of seconds to run and if more input is received then the request won't be handled because its in the middle of the method. For example:
private static BufferedReader in;
private static PrintWriter out;
public static void main(String[] args) {
Main main=new Main();
main.createWindow();
try {
Socket init = new Socket(InetAddress.getLocalHost(), 4001);
int port
=Integer.parseInt
(new BufferedReader
(new InputStreamReader(init.getInputStream())).readLine());
init.close();
Socket socket=new Socket(InetAddress.getLocalHost(), port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
try {
Thread.sleep(5000);
out.println("Give me some data");
if(in.readLine().equals("Have some data")){
System.out.println("Data recieved");
}else{
System.out.println("Data not recieved");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void createWindow(){
JFrame frame =new JFrame("This is a button");
Container pane=getContentPane();
JButton button=new JButton("This is a button");
button.addActionListener(this);
pane.add(button);
frame.setTitle("Messaging");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(pane);
frame.setVisible(true);
frame.setSize(400, 350);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button press");
try {
out.println("Button Press");
if(in.readLine().equals("Button acknowledged")){
System.out.println("Button complete");
}else{
System.out.println("Button fail");
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
If the button is pressed whilst the server is fetching data it conflicts and the wrong response is sent. So how would I deal with this? If I create a separate socket to just handle automated checks that's double the amount of threads the server has to cope with. Is there a better solution?
And please can you try to explain as this is a new area for me.
Your problem is that you have two threads interacting with the socket, your main thread and the Swing event handling thread. This means that the two threads can queue two different things, and the responses may be picked up by the wrong thread. The simplest way to get where you want to be is to put all the socket interaction in one thread.
There are two ways to do this. One would be for the main thread to queue the periodic automated checks to the Swing event handling thread. However, that's a bit complicated and also possibly buggy as the actual Swing threading model is different from what's documented.
The other way to do it would be for the Swing event handling thread to queue button presses to the main thread. In this case, you would queue the button press to the main thread using proper synchronization. The main loop of the main thread would check for button presses and send them through the socket and wait for the proper response.

Time shift in updating JavaFX GUI with actual position from Arduino/Grbl

I want to show the actual position of a machine controlled with an arduino and installed grbl in JavaFX. Therefore I have a timer in the FXML Controller to send a command (?) every 200 milliseconds via the serial terminal to the arduino/grbl.
FXML Controller
Timer timer = new Timer();
TimerTask updatePos = new TimerTask() {
#Override
public void run() {
grblSender.sendLine("?");
}
};
timer.schedule(updatePos, 200, 200);
Then I have a Listener class to catch the incoming responds from the serial terminal to process them.
GrblListener
class GrblListener implements SerialPortEventListener {
#Override
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR() && event.getEventValue() > 0){
try {
String input = GrblSender.serialPort.readString();
System.out.println(input.trim());
if(input.startsWith("<Idle"))
Main.controller.updateCurrentPosition(input.trim());
}
catch (SerialPortException ex) {
System.out.println(ex);
}
}
}
}
In the FXML controller I want to update textFields for x,y and z position with the method updateCurrentPosition.
FXML Controller
public void updateCurrentPosition(String position){
try{
String[] pos = position.split(":");
try{
String mPos = pos[1];
try{
String[] mPosSplit = mPos.split(",");
textFieldPositionX.setText(mPosSplit[0]);
textFieldPositionY.setText(mPosSplit[1]);
textFieldPositionZ.setText(mPosSplit[2]);
}catch(Exception e){}
}catch(Exception e){}
}catch(Exception e){}
}
The problem is, that the GUI is only updated after a command is finished.
E.g. I send the command: G0 X10
Then the GUI is showing the correct position after the command (X:10, Y:0, Z:0), but not the changing position while the processing of the command.
Output of the console is correct and NOT time shifted:
<Run,MPos:0.008,0.000,0.000,WPos:0.008,0.000,0.000>
ok
<Run,MPos:0.292,0.000,0.000,WPos:0.292,0.000,0.000>
ok
<Run,MPos:0.976,0.000,0.000,WPos:0.976,0.000,0.000>
ok
<Run,MPos:2.068,0.000,0.000,WPos:2.068,0.000,0.000>
ok
<Run,MPos:3.380,0.000,0.000,WPos:3.380,0.000,0.000>
ok
<Run,MPos:4.332,0.000,0.000,WPos:4.332,0.000,0.000>
ok
<Run,MPos:4.876,0.000,0.000,WPos:4.876,0.000,0.000>
ok
<Idle,MPos:5.000,0.000,0.000,WPos:5.000,0.000,0.000>
ok
<Idle,MPos:5.000,0.000,0.000,WPos:5.000,0.000,0.000>
ok
What's the mistake? Should I use threads and when so how?

Creating new Thread to update JLabel and setIcon() in said separate Thread

Trying to get an image from another application sending an array of bytes through socket, translating it to a BufferedImage and setting a JLabel in the GUI that updates every 3 seconds.
I tried looking it up on forums but questions regarding graphical update are recurrent to me and I never seem to get it right -- there's at least 6 update methods in java for graphical interface and every one I tried won't work.
I know the problem isn't in the connection to the client because I can easily save the image I receive with ImageIO.write() and it updates every 3 seconds to the image I was expecting to receive. I just can't have Java updating the JLabel correctly without having to go to forums and ask people. Such a complex task I guess. Here is the code: http://pastebin.com/95nMGLvZ. I am doing this project in Netbeans so there's a lot of stuff in there that is unnecessary to read as it does not directly relate to the problem.
I think the answer lies in creating a separate thread to update my JLabel from the ever-changing BufferedImage that is received from the ObjectInputStream. Anyone mind giving me a hand at this? What is better for my code? SwingWorker, Threading (wtf is setDaemon(flag)?), Runnable, Timer, invokeLater? Tried all of this. Not correctly apparently.
EDIT1:
Tried your answer immibis:
public void startRunning() {
try {
server = new ServerSocket(666, 10);
connection = server.accept();
networkStatus("Connected to " + connection.getInetAddress().getHostName());
Thread thr = new Thread(new Runnable() {
#Override
public void run() {
try {
input = new ObjectInputStream(connection.getInputStream());
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
}
});
thr.start();
System.out.println(!connection.isInputShutdown());
while (connection.isConnected()) {
try {
byte[] byteImage = (byte[]) input.readObject();
InputStream in = new ByteArrayInputStream(byteImage);
final BufferedImage bi = ImageIO.read(in);
jLabel_screen.setIcon(new ImageIcon(bi));
ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg"));
System.out.println("i'm working");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
}
It does not work. It says byte[] byteImage = (byte[]) input.readObject(); line has a NullPointerException. The only value that can be null is the return from readObject(), meaning either the input was not initialized correctly or the connection is not synchronized. I hope it's the first option because I wouldn't know how to handle the last.
EDIT2:
Tried your answer blazetopher:
public void startRunning() throws IOException {
server = new ServerSocket(666, 10);
try {
connection = server.accept();
networkStatus("Connected to " + connection.getInetAddress().getHostName());
input = new ObjectInputStream(connection.getInputStream());
while (true) {
try {
byte[] byteImage = (byte[]) input.readObject();
InputStream in = new ByteArrayInputStream(byteImage);
final BufferedImage bi = ImageIO.read(in);
SwingUtilities.invokeLater(new Runnable() {//<-----------
#Override
public void run() {
jLabel_screen.setIcon(new ImageIcon(bi));
}
});
ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg"));
System.out.println("i'm working");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (EOFException eofException) {
networkStatus("Connection Closed. :(");
} finally {
input.close();
connection.close();
}
}
Using SwingUtilities.invokeLater didn't work either. At least the program runs and can even save the image but still can't update the JLabel. Am I running out of options here?
EDIT3:
Tried Jordan's code:
#Override
public void paint(Graphics g) {
g.drawImage(biGlobal, 0, 0, null);
}
The GUI kind of crashed and was "drawing" the components just when I had my mouse cursor hovering it. When I started the code, it did not crashed (+1) but it did not draw anything, even when I try to hover the cursor into where the BufferedImage should be painted. Maybe I should add revalidate() or repaint after calling the Overwritten paint(getGraphics()) inside the startRunning() method?
EDIT4: the while(true) that the code is actually in may be the problem but when I use a SwingTimer it gets out of sync with the client and crashes after first cycle. Any alternatives to this?
Generally speaking you have a producer/consumer pattern. Something is producing images and something wants to consume images. Normally, the consumer would wait on the producer to tell it something has been produced, but in this case, we can use a observer pattern instead, having the producer notify the consumer that something was been produced (instead of waiting for it)
We need someway for the producer to communicate with the consumer...
public interface PictureConsumer {
public void newPicture(BufferedImage img);
}
You would create an implementation of this in your UI code, this would then set the icon property of the JLabel
Now, we need something to produce the images...
public class PictureProducer extends SwingWorker<Object, BufferedImage> {
private PictureConsumer consumer;
public PictureProducer(PictureConsumer consumer) {
this.consumer = consumer;
}
#Override
protected void process(List<BufferedImage> chunks) {
// Really only interested in the last image
BufferedImage img = chunks.get(chunks.size() - 1);
consumer.newPicture(img);
}
#Override
protected Object doInBackground() throws Exception {
/*
This whole setup worries me. Why is this program acting as the
server? Why aren't we pooling the image producer?
*/
try (ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(666, 10)) {
try (Socket socket = server.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
// Using `while (true)` is a bad idea, relying on the fact
// that an exception would be thrown when the connection is closed is
// a bad idea.
while (!socket.isClosed()) {
// Generally, I'd discourage using an ObjectInputStream, this
// is just me, but you could just as easily read directly from
// the ByteArrayInputStream...assuming the sender was sending
// the data as a byte stream ;)
byte[] bytes = (byte[]) ois.readObject();
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
BufferedImage img = ImageIO.read(bis);
publish(img);
}
}
}
}
}
return null;
}
#Override
protected void done() {
try {
get();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Image Producer has failed: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
See Worker Threads and SwingWorker for more details
You can a reverse example of this (where some server is producing images and a client is consuming them) here
To update your label, you want to ensure you're using the EDT thread, so use SwingUtilities.invokeLater from the code where you're receiving the BufferedImage (which would ideally be in a separate "worker" thread:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Update your label here
}
});
What is it you are attempting to accomplish.there might be a better way all together.
For example replace the JLabel with a JPanel then use that JPanel paint method
#Override
public void paint(Graphics g) {
g.drawImage(Img, xcord, ycord, null);
}
Then make that JPanel implement runnable and do your updates in that run method.
JPanel class
public class GraphicsPanel extends JPanel{
private BufferedImage img;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D G2D = (Graphics2D) g;
G2D.drawImage(img, 0, 0, null);
}
public void setImg(BufferedImage img) {
this.img = img;
}
}
then make sure this panel is visible from wherever you wish to call its methods.
this will look something like this
GraphicsPanel graphicsPanel = new GraphicsPanel();
boolean running;
BufferedImage srcImage;
public void run(){
while(running){
graphicsPanel.setImg(srcImage);
graphicsPanel.repaint();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

How to populate GUI with ethernet data? (Java)

Currently developing a GUI in Java that communicates through Ethernet. It is designed to send data through one port and receive data through the other. The data received is used to populate a dynamic section of the GUI that changes with the data received that unfortunately must also be scaled to fit on the screen. My question is how using Java would I go about populating part of the GUI with a scaled version of the Ethernet data received?
Using swing for the display and awt for the listener. Data is received from java socket. Still unclear on exact way received data is packaged but trying to get a base idea on how to populate the virtual screen. separate thread to populate the screen most likely but also how to actually populate the screen with scaled version.
The GUI is designed to emulate a physical display unit with physical buttons and graphical display (colored words as well as arrows and other graphics, original idea was to just map it pixel for pixel but scaling throws me off).
Here is a snippet of the code, the virtual screen is not populated at the moment, just white outline and the communication port is not opened yet (all McduPanel does is paint static image to the screen, and setKey just sets a String variable based on the button pressed and sends it through the open port):
private McduPanel mPanel;
public McduGui() {
mPanel = new McduPanel();
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice vc = env.getDefaultScreenDevice();
JFrame window = new JFrame();
window.addMouseListener(new MouseClickHandler());
window.add(mPanel);
window.setUndecorated(true);
window.setResizable(false);
vc.setFullScreenWindow(window);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
getPorts();
} catch (IOException ex) {
System.out.println(ex);
}
}
public static class MouseClickHandler implements MouseListener {
#Override
public void mousePressed(MouseEvent me) {
int screenX = me.getXOnScreen();
int screenY = me.getYOnScreen();
System.out.println("screen(X,Y) = " + screenX + "," + screenY);
setKey(screenX,screenY);
}
#Override
public void mouseReleased(MouseEvent e) {
}
#OverrideDo
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
public static void getPorts() throws IOException {
String screen = "host"; //host name
String charData = "host"; //host name
int screenPort = 71;//Integer.parseInt(71);
int charPort = 72;//port number
Socket echoSocket = new Socket(screen, 71);
PrintWriter out = new PrintWriter(echoSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
}
To further clarify:
A static image is painted to the screen with Strings mapped to positions on the screen that will send the String associated to that position every time it sees a mouse press. There is a virtual screen within the GUI that will change based on data received from port. It is up in the air what that data will be right now, but by design it is supposed to be the exact same as what physical screen looks like.
Best example I can give is think of a gameboy, the gameboy image is static on the panel, but virtual screen keeps updating based on the data received from the port. That box needs to be refreshed and more than likely scaled from the original size.
OK. I really tried hard to understand your problem. I am still not sure if your problem is painting, threads or reading from a socket, but I made a runnable example. The code is super ugly, but you can execute it as it is. The code create a "test server" which writes to a port every some seconds and a "test client" which reads from this port every some seconds. After the client read some text, it will print it on the screen.
Note that this is just an example - super ugly as I said, but it runs right out of the box. Also not that I strongly recommend to not paint any stuff! Use JButtons to display buttons and JTextFields (or JTextAreas) to display text. Do not print it by painting on the screen!
import java.awt.Graphics;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RemoteConsole extends JPanel {
private String text = "Start";
public static void main(String[] args) throws Exception {
// your class is called McduPanel
RemoteConsole remoteConsole = new RemoteConsole();
// JFrame which contains all components
JFrame frame = new JFrame();
frame.setSize(400, 500);
frame.setVisible(true);
frame.getContentPane().add(remoteConsole);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ServerSocket serverSocket = new ServerSocket(4564);
// this is a test server which just writes "test"
// to a port every 1 seconds
new Thread() {
public void run() {
try {
Socket socket = new Socket("localhost", 4564);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
while (true) {
System.out.println("send");
out.println("test");
sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
// a test client which checks the port every 1.5 seconds
// and paints the text to a JPanel
new Thread() {
public void run() {
try {
Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String message = in.readLine();
System.out.println("received: " + message);
remoteConsole.appendText(message);
remoteConsole.repaint();
sleep(1500);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(text, 75, 100);
}
public void appendText(String text) {
this.text += text;
}
}

Java Networking Server, protocol and client

I have this multi server thread, so whenever the 'X' button is click on the top right hand corner, i want to display the message "closing the socket" on the server side.
public void run
{
try
{
// .....
String inputLine, outputLine;
Protocol p = new Protocol();
outputLine = p.processInput(null, p);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = p.processInput(inputLine,p);
out.println(outputLine);
if (outputLine.equals("Bye"))
{
System.out.print("Closing socket.");
break;
}
}
out.close();
in.close();
socket.close();
catch (IOException e)
{
// .....
}
}
public String processInput(String theInput, Protocol p)
{
theOutput = null;
frame = new JFrame ("Find The Word!");
frame.addWindowListener( new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
theOutput = "Bye";
}
});
frame.add(p);
frame.pack();
frame.setVisible(true);
return theOutput;
}
But it doesn't work, and i have to manually end the process for the server side. I assume that it got stuck at the while loop, but i couldn't figure out whats wrong with it.
Assuming you can't induce the server to send you a Bye messages, you can drop out of the loop as if it had sent you a Bye message or close the socket from another thread.

Categories