How to populate GUI with ethernet data? (Java) - 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;
}
}

Related

How do I make a JButton display 2 different modes every time I click it?

I am trying to implement a JButton that displays Connect or Disconnect depending on whether or not it is connected to the server. So, when I click the button when it says Connect, it'll connect to the server, and then it will show Disconnect. When I click Disconnect, it'll disconnect from the server and the button will show connect again. However, when I click the button, nothing happens.
btnConnect.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
if (btnConnect.getText().equals("Connect")){
btnConnect.setText("Disconnect");
try {
int portNum = 5520;
String hostAddress = Actual_IP_Address.getText();
sock = new Socket(hostAddress, portNum);
writeSock = new PrintWriter ( sock.getOutputStream(), true);
readSock = new BufferedReader (new InputStreamReader(sock.getInputStream()));
}
catch(Exception ex) {
System.out.println("Error: " + ex);
sock = null;
}
}
if (btnConnect.getText().equals("Disconnect")){
btnConnect.setText("Connect");
try {
readSock.close();
writeSock.close();
sock.close();
sock = null;
}
catch(Exception ex) {
System.out.println("Error: " + ex);
sock = null;
}
}
}}
);
How come when I click the button it merely shows Connect?
Right now, the basic outline of your code is
if (button.getText().equals("Connect"){
button.setText("Disconnect")
}
if (button.getText().equals("Disconnect"){
button.setText("Connect")
}
It looks like what is happening is that you are changing your button text value to "Disconnect" like you should be. But then immediately after, you once again check if the button text value is equal to "Disconnect" and change it again. Change your two if statements to be an else-if statement instead
if (button.getText().equals("Connect"){
button.setText("Disconnect")
} else if (button.getText().equals("Disconnect"){
button.setText("Connect")
}
This "basic" problem is, when connected, you set the button text to Disconnect and then promptly check to see if the text is Disconnect and set it back to Connect.
The larger problem is you're coupling your code, which will make it hard to manage and keep the UI in sync with what is actually going on - what happens if the socket disconnects unexpectedly.
Another issue is, you're potentially, blocking the UI, which cause it to "freeze" and become unresponsive, as it might take a few seconds for the socket to establish.
A better solution would be to try and decouple the UI from the socket and rely on some kind of observer pattern so you could be notified when the socket state changes, regardless of whether the UI stopped the socket or it stopped due to some other condition.
Personally, I'd use a SwingWorker for this, as it allows you to overload long running/blocking operations to another thread, but provides a number of useful mechanisms for delivering updates back to the UI thread (ie the Event Dispatching Thread) safely, for example...
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JButton doStuff;
private SocketWorker worker;
public TestPane() {
setLayout(new GridBagLayout());
doStuff = new JButton("Connect");
doStuff.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (worker == null) {
doStuff.setText("...");
worker = new SocketWorker("hostname");
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
System.out.println(worker.getState());
switch (worker.getState()) {
case STARTED:
doStuff.setText("Disconnect");
break;
case DONE:
worker = null;
doStuff.setText("Connect");
break;
}
}
}
});
worker.execute();
} else {
System.out.println("...");
try {
worker.stop();
} catch (IOException ex) {
ex.printStackTrace();
}
doStuff.setText("Connect");
}
}
});
add(doStuff);
}
}
public class SocketWorker extends SwingWorker<Void, Void> {
private Socket socket;
private String hostName;
private PrintWriter writeSock;
private BufferedReader readSock;
public SocketWorker(String hostName) {
this.hostName = hostName;
}
#Override
protected Void doInBackground() throws Exception {
// This is just here to demonstrate the point
Thread.sleep(5000);
//int portNum = 5520;
//socket = new Socket(hostName, portNum);
//writeSock = new PrintWriter(socket.getOutputStream(), true);
//readSock = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Do your socket handling here...
return null;
}
public void stop() throws IOException {
if (socket == null) { return; }
socket.close();
}
}
}
In this example, I've just used Thread.sleep to put a "mocked" operation in place. I'd consider doing all your reading/writing to the socket through the worker and keep it alive until the socket it closed (for what ever reason). But, this will demonstrate how the button can be updated no based on the state of the click, but based on the state of the worker itself
Take a look at Worker Threads and SwingWorker and Concurrency in Swing for more details

Cant append to JTextArea

I'm trying to create a text chat with Java. I have a Server and a Client that connect to each other using Streams, and send data using the objectInputStream and objectOutputStream.
I have GUI's for both the client and the server.
I made these GUI's using intellij's GUI Form.
server GUI form image
The problem I'm having is when I try to display text to the GUI of the server. I can append to the GUi if I call my relayToAll method from the JTextField actionlistener, which then send the message to all the clients and prints it out in the servers GUI.
If i try to call the same method from where I receive the input, then the append to the text area does not work.
Can anyone tell me why its not appending?
Thanks
public class ServerTest {
private JTextField textField1;
private JTextArea textArea1;
private JPanel Panel;
static private ObjectOutputStream objectOutputStream;
static private ObjectInputStream objectInputStream;
static private Socket client;
static private ArrayList<Socket> clients = new ArrayList<Socket>();
static private ArrayList<ObjectOutputStream> objectOutputStreams = new ArrayList<>();
public void relayToAll(String message){
try {
for(int i = 0; i < clients.size(); i++) {
ObjectOutputStream output = objectOutputStreams.get(i);
output.writeObject(message);
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
appendTextArea(message);
}
public void appendTextArea(String text){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.out.println("This should go to the Server GUI: " + text);
textArea1.append(text + "\n");
}
});
}
public ServerTest() {
textField1.addActionListener(e -> {
System.out.println(e.getActionCommand());
relayToAll(e.getActionCommand());
textField1.setText("");
});
}
public void ReadInput(ObjectInputStream input, int port){
try {
String oldMessage = "";
while (true) {
String message = (String) input.readObject();
if (message != oldMessage){
System.out.println(port + ": " + message);
oldMessage = message;
relayToAll(port + ": " + message);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void IOSetup(){
try {
ServerSocket serverSocket = new ServerSocket( 6969 );
ExecutorService executor = Executors.newFixedThreadPool(5);
System.out.println("server on\n");
for (int i = 0; i < 5; i++){
client = serverSocket.accept();
clients.add(client);
System.out.println("Connection from: "+ client.getPort());
objectOutputStream = new ObjectOutputStream(client.getOutputStream());
objectOutputStreams.add(objectOutputStream);
objectInputStream = new ObjectInputStream(clients.get(i).getInputStream());
executor.submit(() -> {
ReadInput(objectInputStream, client.getPort());
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Server");
frame.setContentPane(new ServerTest().Panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
ServerTest application = new ServerTest();
application.IOSetup();
}
Actually you've got kind of a silly mistake. Please check lines (A) and (B) below:
public static void main(String[] args) {
JFrame frame = new JFrame("Server");
frame.setContentPane(new ServerTest().Panel); // *************** (A)
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
ServerTest application = new ServerTest(); // *************** (B)
application.IOSetup();
}
Do you see a problem? You're creating TWO ServerTest objects, one that has its Panel variable added to the JFrame and that gets displayed, and the other that is set up for IO communication. The ActionListener changes the state of the displayed JTextArea while the IO communications changes the state of a JTextArea that is in the second ServerTest instance, the one not displayed.
One improvement is to create only one instance:
public static void main(String[] args) {
ServerTest application = new ServerTest(); // create one instance
JFrame frame = new JFrame("Server");
// frame.setContentPane(new ServerTest().Panel);
frame.setContentPane(application.Panel); // and use in both places
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
//ServerTest application = new ServerTest();
application.IOSetup(); // and use in both places
}
Other problems:
You've got long-running code long running and blocking in a background thread, and that is potentially dangerous, and the only reason that your GUI is not getting frozen is because you're starting the GUI (incorrectly) on the main thread and off of the Swing event thread. For more on this, you will want to read up on Swing concurrency: Lesson: Concurrency in Swing
You will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

How to implement a send button in a chat client GUI

I have implemented a chat client. And as of now it only fully works in cmd, because I can't figure out how I'm supposed to implement the send button in the chat client GUI.
So this is my chat client class:
class ChatClientClass {
private String host;
private int port;
private Socket socket;
private PrintWriter out;
private BufferedReader in;
private BufferedReader stdIn;
ChatWindow chatWindow = new ChatWindow();
public ChatClientClass(String host, int port) {
try {
this.socket = new Socket(host, port);
this.out =
new PrintWriter(socket.getOutputStream(), true);
this.in =
new BufferedReader(
new InputStreamReader(socket.getInputStream()));
this.stdIn =
new BufferedReader(
new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput);
chatWindow.addText("You: " + userInput);
String serverMessage = in.readLine();
chatWindow.addText(serverMessage);
}
} catch (UnknownHostException e) {
System.err.println("Couldn't not find host " + host);
e.printStackTrace();
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " + host);
e.printStackTrace();
System.exit(1);
}
}
}
As of now, I can only write stuff in the command prompt. But what I've written as well as what the server answers will appear in my GUI. The addText(String text)-method adds the input and the output to my GUI.
But I can't figure out how to implement my send button. An easy way would be if I could just send a reference to the PrintWriter and a reference to the GUI when I call the constructor of my ActionListener class, and just do something like: thePrintWriter.println( [get the value of the text area that belongs to the send button] ) in the public void actionPerformed(ActionEvent e)-method. But since I can't/shouldn't call the constructor of my ActionListener from my chat client class, hence sending those references. That wont be possible, right?
I also thought about making the PrintWriter variable static, and also making the JTextArea containing the message I want to send variable static, and then create static methods to access these two variables. But it just feels like I'm doing something terribly wrong when I'm doing that. And I can't get that to work either.
So how is a send button in a chat client supposed to be implemented?
Thanks in advance!
If you are new in GUI building in java/eclipse.
I suggest you the gui builder:
http://www.eclipse.org/windowbuilder/
Its really easy to use, and you can make simple GUI-for your app.
To your problem you will need a button to your frame, and you need to add an actionlistener than if you fire it you can do what you want.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class ButtonAction {
private static void createAndShowGUI() {
JFrame frame1 = new JFrame("JAVA");
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton(" >> JavaProgrammingForums.com <<");
//Add action listener to button
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
//Here call your sender fucntion
out.println(userInput);
chatWindow.addText("You: " + userInput);
String serverMessage = your_jtext.getText();
chatWindow.addText(serverMessage);
System.out.println("You clicked the button");
}
});
frame1.getContentPane().add(button);
frame1.pack();
frame1.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

Why does calling the main method of a different class not work correctly?

I'm in the process of writing a game. The game plays background music while it is running. This works fine, and I've decided to add a main menu, as their are three types of this game:
Single Player
Two Player
Online
When I run these classes individually (which have their own main methods - obviously), they work perfectly fine. However, in my Welcome Menu class, which is responsible for the main menu (all necessary imports are there, just not shown here):
public class WelcomeMenu implements ActionListener {
public void setButtonBG(JButton button, String imgPath) throws IOException //this method is reponsible for setting images to their corresponding JButton(s)
{
BufferedImage img = ImageIO.read(ClassLoader.getSystemResource(imgPath));
ImageIcon sp = new ImageIcon(img);
button.setIcon(sp);
button.setBorderPainted(false);
}
private JFrame welcomeWindow = new JFrame("Tic-Tac-Toe");
private JButton singlePlayerButton = new JButton();
private JButton twoPlayerButton = new JButton();
private JButton onlineButton = new JButton();
public WelcomeMenu() throws IOException
{
//START OF CONSTRUCTOR
//Main window is being sized, default way to close, and internal layout
welcomeWindow.setSize(600, 420);
welcomeWindow.setLayout(new CardLayout());
//Object res = this.getClass().getResource("/");
//System.out.println(res);
BufferedImage bf = ImageIO.read(ClassLoader.getSystemResource("images/mainMenuBG.jpg"));
welcomeWindow.setContentPane(new backImage(bf)); // adding created component to the JFrame using the backImage class
welcomeWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
welcomeWindow.setLocationRelativeTo(null);
welcomeWindow.setResizable(false);
welcomeWindow.setVisible(true);
//setting the icon
try
{
java.net.URL url = ClassLoader.getSystemResource("images/icon.png");
Toolkit kit = Toolkit.getDefaultToolkit();
Image img = kit.createImage(url);
welcomeWindow.setIconImage(img);
}
catch(NullPointerException n)
{
System.out.println("Image could not be fetched.");
}
//adding custom buttons
//ImageIcon singlePlayer = new ImageIcon("images/singlePlayerButton.jpg");
//setting sizes
singlePlayerButton.setSize(387, 72);
twoPlayerButton.setSize(387, 72);
onlineButton.setSize(387, 72);
//setting background images to buttons
setButtonBG(singlePlayerButton, "images/sPlayerButton.jpg");
setButtonBG(twoPlayerButton, "images/tPlayerButton.jpg");
setButtonBG(onlineButton, "images/mPlayerButton.jpg");
//adding listeners
singlePlayerButton.addActionListener(this);
twoPlayerButton.addActionListener(this);
onlineButton.addActionListener(this);
//adding the custom buttons
welcomeWindow.add(singlePlayerButton);
welcomeWindow.add(twoPlayerButton);
welcomeWindow.add(onlineButton);
//setting locations and visibility
singlePlayerButton.setLocation(110, 90);
singlePlayerButton.setVisible(true);
twoPlayerButton.setLocation(110, 182);
twoPlayerButton.setVisible(true);
onlineButton.setLocation(110, 274);
onlineButton.setVisible(true);
//END OF CONSTRUCTOR
}
public static TicTacToeTP spg;
//All actions are done here
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == singlePlayerButton)
{
System.out.println("<LOG> SINGLE PLAYER GAME REQUESTED");
JOptionPane.showMessageDialog(welcomeWindow, "This game mode has not been implemented yet.");
}
if(e.getSource() == twoPlayerButton)
{
System.out.println("<LOG> TWO PLAYER GAME REQUESTED");
try
{
//spg = new TicTacToeTP("images/black-squareMod_RED.jpg");
//spg.playBackgroundSong();
TicTacToeTP.main(null);
}
catch(IOException io)
{
System.out.println("IO EXCEPTION!");
}
welcomeWindow.setVisible(false);
welcomeWindow.dispose();
}
if(e.getSource() == onlineButton)
{
System.out.println("<LOG> ONLINE GAME REQUESTED");
JOptionPane.showMessageDialog(welcomeWindow, "This game mode has not been implemented yet.");
}
}
public static void main(String[] args) throws IOException
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex)
{
}
}
});
new WelcomeMenu();
}
}
... if I click the Two Player button, for example, it plays the audio ONLY. None of my other components load. Just an empty JFrame. Notice how in the actionPerformed() method, I tried both TicTacToeTP.main(null) and (commented out, now) instantiating a new TicTacToeTP object AND calling the playBackgroundSong() method. If I eliminate this methods call, and just instantiate the object, it works fine - but no music.
Why is this happening, and how can I fix it?
Here is the playBackgroundSong() method:
private Player p = null;
//private InputStream fis = null;
public void playBackgroundSong() //responsible for playing background music
{
//PausablePlayer p = null;
InputStream fis = null;
ArrayList<InputStream> stream = new ArrayList<InputStream>(); //this ArrayList contains multiple audio files that the method will loop through >> defined below
stream.add(ClassLoader.getSystemResourceAsStream("resources/01 Intro.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/Basic space - The XX - Instrumental.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/Mirrors [ Upbeat Electronic Instrumental ] Spence Mills HQ Free Beat Download 2012.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/Static [ Aggressive Dark Pop Hip Hop Rap Instrumental ] Spence Mills Free Beat Download Link 2012 HD.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/System Shock 2 soundtrack Med Sci 1.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/System Shock 2 Soundtrack Ops 2.mp3"));
stream.add(ClassLoader.getSystemResourceAsStream("resources/01 Intro.mp3"));
Collections.shuffle(stream);
for(int i = 0; i < stream.size(); i++)
{
try
{
fis = stream.get(i);
}
catch (NullPointerException ex)
{
Logger.getLogger(TicTacToeTP.class.getName()).log(Level.SEVERE, null, ex);
}
try
{
p = new Player(fis);
}
catch (JavaLayerException ex)
{
Logger.getLogger(TicTacToeTP.class.getName()).log(Level.SEVERE, null, ex);
}
try
{
p.play();
}
catch (JavaLayerException ee)
{
Logger.getLogger(TicTacToeTP.class.getName()).log(Level.SEVERE, null, ee);
}
}
playBackgroundSong();
}
You appear to be playing a long-running bit of code, playBackgroundSong(), on the Swing event dispatch thread or EDT. This thread is responsible for painting the GUI and interacting and responding to user input, and if it gets tied up, the program essentially freezes. This might not have been an issue when you called this method in the main method -- basically off of the Swing event thread, but is an issue when it is specifically called on the event dispatch thread. A possible solution: play your music in a background thread. A SwingWorker might work well for you, and there are decent tutorials on the use of these and the EDT. Google "Concurrency in Swing", and check out what will likely be the first hit for more.
As an aside: you usually don't want to call another class's main method. Instead create an instance of the other class and use it.
Edit You state:
Thanks. Looking at this part: docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html Seems to explain what I want to do, correct? I am reading it all, by the way
Actually you could go even simpler. Since you're not waiting for a result from your playBackgroundSong(), you could possibly just call it in its own simple thread by just wrapping it in a Runnable and then putting that in a Thread:
new Thread(new Runnable() {
public void run() {
playBackgroundSong();
}
}).start();

Progress bar in Swing (Java) for command tools

I have several C/C++ command line tools that I'm wrapping with Java.Swing as GUI. The command line tools can take minutes to hours. Progress bar seems like a good idea to keep users sane. I'm also thinking it might be nice to wrap a GUI for the progress bar, instead of just using system out. But how?
I'm thinking the command line tools can write percents to stderr and I can somehow read it in java. Not exactly sure what the mechanics for this would be. I'm also not clear on asynchronous display (learned a bit about invokeLater() ). New to Java, and would appreciate general suggestions as well. Thanks.
--- update ---
Thanks everyone for your suggestions. Here's the resulting code.
private void redirectSystemStreams() {
OutputStream out_stderr = new OutputStream() {
#Override
public void write(final int b) throws IOException {
update(String.valueOf((char) b));
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
update(new String(b, off, len));
}
#Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
};
System.setErr(new PrintStream(out_stderr, true));
}
private void update(final String inputText) {
int value = 20; //parse inputText; make sure your executable calls fflush(stderr) after each fprintf().
jProgressBar.setValue(value);
/* Also one can redirect to a textpane
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//update jTextPane with inputText
}
});
*/
}
That's seems very fragile, better would be to communicate via sockets in a well established protocol or with some sort of RCP ( perhaps Google's protobuf ) or even webservices.
If you still insists you can launch a process in Java with ProcessBuilder that will give you a Process reference of which you can get the InputStream to read the standard output, but again, that seems very fragile to me.
I hope this helps.
For the progress bar part of your problem you can do something like the following. Note that this is just an example to illustrate the point.
Basically, a thread is created to do the work. Presumably this Runner thread will be interacting with your C/C++ code to get its progress. It then calls update on the Progress Bars Dialog class.
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class Main {
private int value;
private Progress pbar;
public static void main(String args[]) {
new Main();
}
public Main() {
pbar = new Progress();
Thread t = new Thread(new Runner());
t.start();
}
class Progress extends JDialog {
JProgressBar pb;
JLabel label;
public Progress() {
super((JFrame) null, "Task In Progress");
pb = new JProgressBar(0, 100);
pb.setPreferredSize(new Dimension(175, 20));
pb.setString("Working");
pb.setStringPainted(true);
pb.setValue(0);
label = new JLabel("Progress: ");
JPanel panel = new JPanel();
panel.add(label);
panel.add(pb);
add(panel, BorderLayout.CENTER);
pack();
setVisible(true);
}
public void update(){
pb.setValue(value);
if(value >= 100){
this.setVisible(false);
this.dispose();
}
}
}
class Runner implements Runnable {
public void run() {
for (int i = 0; i <= 100; i++) {
value++;
pbar.update();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
}
}
// Create a window
JFrame frame = new JFrame("Progress");
// Creates a progress bar and add it to the window
JProgressBar prog = new JProgressBar();
frame.add(prog);
// Run C/C++ application
try {
Process p = Runtime.getRuntime().exec(new String[]{"filename","arg1","arg2","..."});
// Get InputStream
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
// Update the progress when recieving output from C/C++
new java.util.Timer().schedule(new TimerTask(){
public void run(){
String str = "";
while ((str=br.readLine()!=null) {
prog.setValue(new Integer(str)); // Set Value of Progress Bar
prog.setString(str+"%"); // Set Value to display (in text) on Progress Bar
}
}
},0,100); // Check every 100 milliseconds
// Fit the window to its contents and display it
frame.pack();
frame.setVisible(true);
} catch (Exception e) {
System.out.println("Failed To Launch Program or Failed To Get Input Stream");
}

Categories