Displaying a loading JFrame while a loop is running in main thread - java

this is my first post so please inform me if I'm doing anything wrong.
I've been working on a mini-applet to get the status of several nodes. This works by having a thread of each node which checks its uptime in a network class. Once it gets the status of the node it'll put it into a hashmap. I then check the status of that hashmap and wait till its content amount is equal to the total amount of nodes that we're checking. Once done it proceeds to display them on a screen. The problem is despite making this a heavily threaded application fetching the status for all nodes still takes a few seconds. During this time I want to display a separate JFrame with a loading bar (or even use the same JFrame if it would work.
I'm not going to lie I'm a Java newbie and the code is horrible, I'm wondering if you guys could give me advice as to what I should do. Here is the code.
--main node status window--
public NodeStatusWindow() {
super(Managment.getTitle() + " - Nodes");
setLayout(new FlowLayout());
Thread threada = new Thread(new ShowLoading());
threada.start();
System.out.println("Checking node status...");
Nodes.nodeOnlineCurrent.clear();
for (int i = 0; i < Nodes.getNodes().size(); i++) {
Object currentNode = Nodes.getNodes().keySet().toArray()[i];//
Thread thread = new Thread(new CheckUptime((int) currentNode));
thread.start();
}
while (Nodes.nodeOnlineCurrent.size() < Nodes.getNodes().size()) {
System.out.println(Nodes.nodeOnlineCurrent.size() + " < " + Nodes.getNodes().size());
}
for (int i = 0; i < Nodes.nodeOnlineCurrent.size(); i++) {
JLabel[] nodeList = new JLabel[Nodes.getNodes().size()];
Object currentNode = Nodes.getNodes().keySet().toArray()[i];
nodeList[i] = new JLabel("Node: " + currentNode.toString());
nodeList[i].setOpaque(true);
if (Nodes.nodeOnlineCurrent.get(currentNode)[0] == false) {
nodeList[i].setForeground(new Color(255, 0, 0)); //Red
} else if (Nodes.nodeOnlineCurrent.get(currentNode)[1] == false) {
nodeList[i].setForeground(new Color(230, 222, 0)); //Yellow
} else {
nodeList[i].setForeground(new Color(0, 230, 0)); //Green
}
add(nodeList[i]);
}
--Loading window. This class is created in the threada object in the class above--
public class NodeStatusLoadingWindow extends JFrame {
private static final long serialVersionUID = 1369511477485416862L;
private JLabel status;
public NodeStatusLoadingWindow() {
super(Managment.getTitle() + " - Nodes");
setLayout(new FlowLayout());
status = new JLabel("Loading...");
add(status);
}
public void closeWindow() {
this.setVisible(false);
this.dispose();
}
}
Here is the showLoading thread
public class ShowLoading implements Runnable {
public void run() {
try {
GUIHandler.drawNodeStatusLoadingWindow();
} catch (Exception e) {
System.out.println("Exception! " + e.toString());
}
}
}

Related

How to wait a thread to finish to back to the previous code

I have a question regarding threads. I have a method that reads an Excel spreadsheet and saves the data in a database. However, this method also checks the contents of a cell is an expected value, and if not, a frame must be called for the user to choose the most appropriate option. As it is written the code, it is not waiting for the frame open for the user to choose an option so he returns to the initial loop.
(For the post would not be too long, I have omitted parts of the code, leaving only what matters)
The following parts of the code:
public HashMap<String, Historico> importaDadosDoExcel(){
HashMap<String, Historico> mapa = new HashMap<String, Historico>();
HSSFCell cell= null;
HSSFRow row = null;
Historico update = new Historico();
int rowsCount = 0;
String[] statusStr = {"Aguardando Boleto", "Aguardando Lançamento", "Em Workflow","Liberado para Tesouraria", "Pago", "Outros"};
String aux;
for (int i = 1; i <= rowsCount; i++) {
cell = row.getCell(ActvUtils.u.devolveNumColuna("D"));
aux = ActvUtils.u.devolveCampoLido(cell);
if (Arrays.asList(statusStr).contains(aux)) {
update.setStatus(aux);
}else{
//Here, I would like the frame was called (passing as a parameter the value read in the cell) to which the user then chooses the best option, then, that choice was setted in the object.
Runnable runnable = new EscolheStatus(aux);
Thread thread = new Thread(runnable);
thread.start();
//Here, I would like to read the option that the user chose on a string, some like:
// String str = runnable.getStatus();
}
}
return mapa;
}
And now, my frame class:
public class EscolheStatus extends JFrame implements ActionListener,Runnable{
private String[] statusStr = {"Aguardando Boleto", "Aguardando Lançamento", "Em Workflow","Liberado para Tesouraria", "Pago", "Outros"};
private JRadioButton boleto;
private JRadioButton lancamento;
private JRadioButton workflow;
private JRadioButton tesouraria;
private JRadioButton pago;
private JRadioButton outros;
private String status;
private String statusEncontrado;
public EscolheStatus(String statusEncontrado){
this.statusEncontrado = statusEncontrado;
boleto = new JRadioButton(statusStr[0]);
boleto.addActionListener(this);
lancamento = new JRadioButton(statusStr[1]);
lancamento.addActionListener(this);
workflow = new JRadioButton(statusStr[2]);
workflow.addActionListener(this);
tesouraria = new JRadioButton(statusStr[3]);
tesouraria.addActionListener(this);
pago = new JRadioButton(statusStr[4]);
pago.addActionListener(this);
outros = new JRadioButton(statusStr[5]);
outros.addActionListener(this);
ButtonGroup group = new ButtonGroup();
group.add(boleto);
group.add(lancamento);
group.add(workflow);
group.add(tesouraria);
group.add(pago);
group.add(outros);
JPanel radioPanel = new JPanel();
radioPanel.setLayout(new GridLayout(6, 1));
radioPanel.add(boleto);
radioPanel.add(lancamento);
radioPanel.add(workflow);
radioPanel.add(tesouraria);
radioPanel.add(pago);
radioPanel.add(outros);
radioPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Status '" + statusEncontrado + "' não reconhecido. Escolha:" ));
setContentPane(radioPanel);
pack();
this.setSize(350, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
teste();
}
#Override
public void actionPerformed(ActionEvent ev) {
Object opcao = ev.getSource();
if(opcao.equals(boleto)){
status = statusStr[0];
}else if(opcao.equals(lancamento)){
status = statusStr[1];
}else if(opcao.equals(workflow)){
status = statusStr[2];
}else if(opcao.equals(tesouraria)){
status = statusStr[3];
}else if(opcao.equals(pago)){
status = statusStr[4];
}else if(opcao.equals(outros)){
status = statusStr[5];
}
this.dispose();
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Override
public void run() {
new EscolheStatus(statusEncontrado);
}
}
Here's an example of what MasterBlaster suggested (if it's only to stop the thread to require user input):
Thread worker = new Thread(new Runnable() {
#Override
public void run() {
long start = System.currentTimeMillis();
while( (System.currentTimeMillis() - start) < 10000) {
System.out.println( "opening dialog" );
String result = JOptionPane.showInputDialog( "Tell me something!" );
System.out.println("user entered: " + result);
}
System.out.println("finished");
}
});
worker.run();
This will open a dialog and wait for the input again and again until the thread ran for at least 10 seconds.
You should be able to adapt that to your needs.
Of course, if multiple (worker) threads need to communicate you could use Thread.join() to wait for the other thread to finish or wait() and notify() if both threads need to run in parallel and one would just have to finish some task and then continue.
Edit: // String str = runnable.getStatus(); implies you want to get some information about a still running thread. In that case you could use a shared object to write to and read from (you might need some synchronization).

Thread.sleep() freezes JFrame/GUI containing GraphStream graph

My JFrame containing an embedded single graph (Graphstream) freezes when I try to update it in a loop that calls Thread,sleep(). I have tried using the same update on a standalone-graph (displayed on it's own) and it works as expected.
I have a single graph embedded in JFrame as follows (AppGraph.java):
public static ViewPanel init(){
graph.addAttribute("ui.stylesheet", styleSheet);
graph.setAutoCreate(true);
graph.setStrict(false);
graph.addAttribute("ui.quality");
graph.addAttribute("ui.antialias");
initGraph();
initNodes(graph);
return attachViewPanel();
}
private static ViewPanel attachViewPanel() {
Viewer viewer = new Viewer(graph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
viewer.enableAutoLayout();
return viewer.addDefaultView(false);
}
private static void initGraph(){
FileSource fs = new FileSourceDOT();
String graph_filename = "graph.gv";
String absolute_path = System.getProperty("user.home") + File.separator + graph_filename;
fs.addSink(graph);
try {
fs.readAll(absolute_path);
} catch (IOException | NullPointerException e) {
e.printStackTrace();
} finally {
fs.removeSink(graph);
}
}
Then this is called in the JFrame class as below:
/*AppWindow.java
* Set up graph
*/
GridBagConstraints graphConstraints = new GridBagConstraints();
graphConstraints.fill = GridBagConstraints.BOTH;
graphConstraints.gridx = 0;
graphConstraints.gridy = 1;
graphConstraints.weightx = 0.5;
graphConstraints.weighty = 0.5;
graphConstraints.gridwidth = 4;
graphConstraints.gridheight = GridBagConstraints.RELATIVE;
add(AppGraph.init(), graphConstraints);`
On the JFrame are buttons for different search algorithms like BFS. During the execution of these algorithms, edges traversed are colored at fixed time intervals to create a sort of animation effect as shown below:
//BFSAlgorithm.java
private void callBFS(Node startNode, Node goalNode) {
startNode.setAttribute("parent", "null");
startNode.setAttribute("level", 0);
startNode.setAttribute("visited?");
LinkedList<Node> queueFrontier = new LinkedList<>();
int level = 1;
queueFrontier.addLast(startNode);
while (!queueFrontier.isEmpty()) {
System.out.println("Level: " + (level - 1));
LinkedList<Node> next = new LinkedList<>();
for (Node node : queueFrontier) {
if (node == goalNode) {
System.out.println(node.getId() + ": Found Found Found!!!");
if (node != startNode) {
colorEdge(node);
}
return;
}
System.out.print(node.getId() + " visited \t");
if (node != startNode) {
colorEdge(node);
}
for (Edge edge : node.getEdgeSet()) {
Node opposite = edge.getOpposite(node);
if (!opposite.hasAttribute("visited?")) {
System.out.print(opposite.getId() + " enqueued \t");
opposite.setAttribute("level", level);
opposite.setAttribute("parent", node);
opposite.setAttribute("visited?");
next.addLast(opposite);
}
}
System.out.print("\n");
}
level++;
queueFrontier = next;
sleep();
}
}
private void colorEdge(Node node) {
Edge visitedEdge = node.getEdgeBetween(node.getAttribute("parent", Node.class));
visitedEdge.setAttribute("ui.color", 0.5);
sleep();
}
private void sleep() {
try {
Thread.sleep(AppWindow.speed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This BFSAlgorithm implements DynamicAlgorithm and extends SinkAdapter. I have extended the SinkAdapter to enable it to interact with the View as the algorithm runs. When I call the BFSAlgorithm, while the algorithm runs and the various println statements are delayed by sleep(), the GUI freezes and is unresponsive until after execution before all the visited edges are then colored. I tried implementing ViewerListener in my AppGraph.java as is documented on the graphstream documentation but it only resulted in an infinite loop that crashed the application:
/*...init() method from AppGraph.java*/
ProxyPipe fromViewer = viewer.newThreadProxyOnGraphicGraph();
fromViewer.addSink(graph);
fromViewer.pump();
while(loop) {
fromViewer.pump(); //
}
Like #Frakool and #MadProgrammer suggested in the comments, if anyone is having similar issues, using SwingWorker and Swing Timer will provide the desired results. According to the documentation:
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
Here's how I used it to stop the gui freezing. I created a private inner SwingWorker class that uses a Swing Timer as below:
private class BFSTask extends SwingWorker<LinkedList<Node>, Node>{
private ArrayList<Node> visitedList;
private int visitedIndex = 0;
private boolean traversalDone = false;
private Timer traversal = new Timer(AppWindow.speed, new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
Node lastVisited = visitedList.get(visitedIndex);
Edge visitedEdge = lastVisited.getEdgeBetween(lastVisited.getAttribute("parent", Node.class));
visitedEdge.setAttribute("ui.color", 0.5);
visitedIndex++;
if(visitedIndex >= visitedList.size()){
traversal.stop();
traversalDone = true;
if(BFSAlgorithm.this.getPathToGoal() != null){
startTimer();
}
}
}
});
#Override
protected LinkedList<Node> doInBackground() throws Exception {
Node found = publishNodeBreadthFirst(getStartNode(), getGoalNode());
if (found != null) {
return getPathToGoal(found);
} else{
return null;
}
}
#Override
protected void process(List<Node> list) {
visitedList = (ArrayList<Node>) list;
traversal.start();
}
#Override
protected void done() {
try {
BFSAlgorithm.this.pathToGoal = get();
if(traversalDone && BFSAlgorithm.this.getPathToGoal() != null){
startTimer();
}
if(BFSAlgorithm.this.getPathToGoal() == null){
throw new NullPointerException("Goal Not Found.");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (NullPointerException e){
JOptionPane.showMessageDialog(getAppWindow(), "Goal Node Not Found!", "Error", JOptionPane.ERROR_MESSAGE);
getAppWindow().disableExceptClear();
getAppWindow().changeStatus("Goal node not found");
}
}
private LinkedList<Node> getPathToGoal(Node found) {
LinkedList<Node> path = new LinkedList<>();
Node parent = found.getAttribute("parent");
path.addLast(found);
while (parent != getStartNode()){
path.addLast(parent);
parent = parent.getAttribute("parent");
}
return path;
}
}

Thread Applet not Working

I've been working on this for awhile and can't figure out why this applet is not working properly:
I instantiated two threads in my applet.
I created two buttons- start, and stop, which are supposed to change the flag values to end the while() loops in my threads. The applet is not responding to either button. Any suggestions, anyone? Thanks for your time!
This is the applet...
package prodcons;
import java.applet.Applet;
import java.util.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyTableSetting extends Applet {
Soup s; // we will show the soup bowl with the soup's alphabet pieces
int bowlLength = 150; // bowl's dimensions as variables in case we want to change it
int bowlWidth = 220;
int bowlX = 60;
int bowlY = 10;
Producer p1;
Consumer c1;
public void init(){
setSize(400,200); // make the applet size big enough for our soup bowl
s = new Soup(); // instantiate the Soup
p1 = new Producer(this, s); // declare and instantiate one producer thread - state of NEW
c1 = new Consumer(this, s); // declare and instantiate one consumer thread - state of NEW
p1.start(); // start the producer thread
c1.start(); // start the consumer thread
Button stop = new Button("Stop");
stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Producer.producerRunning = false;
Consumer.consumerRunning = false;
Soup.clearBuffer();
}
});
add(stop);
Button start = new Button("Start");
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Producer.producerRunning = true;
Consumer.consumerRunning = true;
p1.run();
System.out.println("heyyy");
c1.run();
}
});
add(start);
}
public void paint(Graphics g){ // first we make the bowl and spoon
int x;
int y;
g.setColor(Color.orange);
g.fillOval(bowlX, bowlY, bowlWidth, bowlLength); // the bowl
g.setColor(Color.cyan);
g.fillOval(10, 25, 40, 55); // the spoon
g.fillOval(25, 80, 8, 75);
g.setColor(Color.black); // black outlines for the dinnerware
g.drawOval(10, 25, 40, 55);
g.drawOval(25, 80, 8, 75);
g.drawOval(bowlX,bowlY, bowlWidth, bowlLength);
ArrayList <String> contents = s.getContents(); // get contents of the soup
for (String each: contents){ // individually add each alphabet piece in the soup
x = bowlX + bowlWidth/4 +(int)(Math.random()* (bowlWidth/2)); // put them at random places to mimic stirring
y = bowlY + bowlLength/4 + (int)(Math.random()* (bowlLength/2));
Font bigFont = new Font("Helvetica", Font.BOLD, 20);
g.setFont(bigFont);
g.drawString(each, x, y);
}
}
}
and these are the threads:
package prodcons;
class Consumer extends Thread {
private Soup soup;
private MyTableSetting bowlView;
static boolean consumerRunning = true;
public Consumer(MyTableSetting bowl, Soup s) {
bowlView = bowl; // the consumer is given the GUI that will show what is happening
soup = s; // the consumer is given the soup--the monitor
}
public void run() {
System.out.println("consuming: "+consumerRunning);
String c;
try {
while(consumerRunning) { // stop thread when know there are no more coming; here we know there will only be 10
c = soup.eat(); // eat it from the soup
System.out.println("Ate a letter: " + c); // show what happened in Console
bowlView.repaint(); // show it in the bowl
sleep((int)(Math.random() * 3000)); // have consumer sleep a little longer or sometimes we never see the alphabets!
}
} catch (InterruptedException e) {
this.interrupt();
}
}
}
and this:
package prodcons;
class Producer extends Thread {
private Soup soup;
private String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private MyTableSetting bowlView;
static boolean producerRunning = true;
public Producer(MyTableSetting bowl, Soup s) {
bowlView = bowl; // the producer is given the GUI that will show what is happening
soup = s; // the producer is given the soup--the monitor
}
public void run() {
String c;
try {
while (producerRunning) { // only put in 10 things so it will stop
System.out.println("thikns producer != null");
c = String.valueOf(alphabet.charAt((int)(Math.random() * 26))); // randomly pick a number to associate with an alphabet letter
soup.add(c); // add it to the soup
System.out.println("Added " + c + " to the soup."); // show what happened in Console
bowlView.repaint(); // show it in the bowl
sleep((int)(Math.random() * 2000)); // sleep for a while so it is not too fast to see
}
} catch (InterruptedException e) {
this.interrupt();
}
}
}
and here is the "Soup" Class:
package prodcons;
import java.util.*;
public class Soup {
private static ArrayList <String> buffer = new ArrayList<String>(); // buffer holds what is in the soup
private int capacity = 6;
public synchronized String eat() { //this method can only be accessed by one thing at a time
while(buffer.isEmpty()){ // cannot eat if nothing is there, so check to see if it is empty
try {
wait(); // if so, we WAIT until someone puts something there
} catch (InterruptedException e) {} // doing so temporarily allows other synchronized methods to run (specifically - add)
} // we will not get out of this while until something is there to eat
String toReturn = buffer.get((int)(Math.random() * buffer.size())); // get a random alphabet in the soup
buffer.remove(toReturn); // remove it so no one else can eat it
buffer.trimToSize(); // reduce the size of the buffer to fit how many pieces are there
notifyAll(); // tell anyone WAITing that we have eaten something and are done
return(toReturn);
}
public synchronized void add(String c) {
while (buffer.size() == capacity) {
try {
wait();
}
catch (InterruptedException e) {}
}
buffer.add(c);
notifyAll();
}
public ArrayList <String> getContents() {
return buffer;
}
public static void clearBuffer() {
buffer.clear();
}
}
Thank you so much!
You call the Producer and Consumer directly on the main UI thread from the event handler for the start button. The only effect that this will have is to freeze your entire application. That's because the main UI thread does everything: event handling, repainting, etc. As long as you hold on to it, there is going to be no event handling, repainting, etc.
You should never call anything that takes long on the main UI thread.
But since you've already started the threads in your init method, you don't need to call run in your action listener. You remove those calls:
Button start = new Button("Start");
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Producer.producerRunning = true;
Consumer.consumerRunning = true;
}
});
Also, you should make the flags volatile, otherwise the change to them from one thread may not be visible in the other thread:
volatile static boolean producerRunning = true;
(etc.)

GUI thread updating issue

I have a GUI class Gui:
public class Gui extends JFrame implements Runnable
{
private JPanel outer, inner;
private JLabel[][] labels = new JLabel[22][12];
private Color[][] defaultMap, map;
Thread t;
private int row, col;
private Color color;
public Gui()
{
Container content = getContentPane();
content.setLayout(new BorderLayout());
setBackground(Color.BLACK);
setSize(1000, 1000);
setLocation(300, 0);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
defaultMap = createMap();
draw(defaultMap);
}
public Color[][] createMap()
{
Color[][] map = new Color[22][12];
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 12; j++)
{
map[i][j] = Color.WHITE;
}
}
for (int i = 0; i < 22; i++)
{
map[i][0] = Color.GRAY;
map[i][11] = Color.GRAY;
}
for (int i = 0; i < 12; i++)
{
map[0][i] = Color.GRAY;
map[21][i] = Color.GRAY;
}
return map;
}
public void draw(Color[][] map)
{
outer = new JPanel();
outer.setLayout(new BorderLayout());
outer.setBackground(Color.WHITE);
outer.setPreferredSize(new Dimension());
inner = new JPanel();
inner.setLayout(new GridLayout(22, 12, 2, 2));
inner.setBackground(Color.BLACK);
for (int i = 0; i < 22; i++)
{
for (int j = 0; j < 12; j++)
{
labels[i][j] = new JLabel();
JLabel label = labels[i][j];
label.setPreferredSize(new Dimension(20, 20));
label.setBackground(map[i][j]);
label.setOpaque(true);
inner.add(label);
}
}
add(outer);
add(inner);
pack();
}
public void move(int row, int col, Color color)
{
System.out.println(row+","+col);
map = defaultMap;
map[row][col] = color;
t = new Thread(this);
t.start();
}
#Override
public void run()
{
draw(map);
}
}
Which is called from my main class like so:
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
gui = new Gui();
gui.setVisible(true);
gui.move(2,5,Color.GREEN);
Thread.sleep(1000);
gui.move(3,5,Color.GREEN);
Thread.sleep(1000);
gui.move(4,5,Color.GREEN);
} catch (InterruptedException ex)
{
Logger.getLogger(Tetris.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
So the weird stuff is happening when the gui.move() function is called. You can ignore the rest or use it if it helps. But each time, a green block should be "added" to the gui at 2,5; 3,5; and 4,5; 1 second after the other.
The issue:
The Gui remains black for a time then immediately repaints/updates with the proper grid and colors with the first two blocks correctly colored green but it's missing the last block at 4,5. Again, the initial "defaultMap" should be painted immediately, but it is not, the JFrame is black until everything is painted together at once minus the last green block. Then each green block should be painted on 1 second after the other.
The interesting part is that the System.out.println() bit in the move method in Gui prints out the row and col as I'd expect it to... They come out about one second after the other in the terminal. So that tells me that something is going right. But I'm not sure what's going on with the Gui...
EDIT: slight difference in story. upon closer examination, I noticed that the last green block does appear for a second as soon as the entire map is painted but immediately "disappears" repainted white.
You are sleeping on the EDT (Event Dispatching Thread). You should never block the EDT.
Never call sleep or wait on the EDT
Move all long running tasks to other Threads (using Executors or SwingWorker)
For repeated or delayed update of the UI, you can use javax.swing.Timer
All UI-operations should be performed on the EDT (using SwingUtilities.invokeLater or javax.swing.Timer or SwingWorker
Read more about Swing concurrency in the swing tag wiki.

Changing JLabels between classes

My first post, so forgive any incorrect etiquette. I'm currently doing my year end project for school and I need a bit of help. I am making a GUI java app in Netbeans. I have two classes. One is a class that controls a timer, the other is a class that is a scoreboard screen. I need to update the scoreboard timerLabel with the time that is being counted down in the timerClass. Its quite messy as there is another timer label in the Timer class which does update. My problem is that I cannot get timerLabel in MatchScreen() to update. Here is my code :
Timer Class
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class TimerClass extends JFrame {
Timer timer;
JLabel promptLabel, timerLabel;
int counter;
JTextField tf;
JButton button;
MatchScreen call = null;
public TimerClass() {
call = new MatchScreen();
setLayout(new GridLayout(4, 4, 7, 7));
promptLabel = new JLabel(""
+ "Enter number of seconds for the timer",
SwingConstants.CENTER);
add(promptLabel);
tf = new JTextField(5);
add(tf);
button = new JButton("Start");
add(button);
timerLabel = new JLabel("waiting...",
SwingConstants.CENTER);
add(timerLabel);
event e = new event();
button.addActionListener(e);
System.out.println("Button pressed");
}
public class event implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Action performed");
int count = (int) (Double.parseDouble(tf.getText()));
timerLabel.setText("Time left: " + count);
call.setTimerLabel(count);
System.out.println("Passed count to tc");
TimeClass tc = new TimeClass(count);
timer = new Timer(1000, tc);
System.out.println("Timer.start");
timer.start();
//throw new UnsupportedOperationException("Not supported yet.");
}
/*public void actionPerformed(ActionEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}*/
}
public class TimeClass implements ActionListener {
int counter;
public TimeClass(int counter) {
this.counter = counter;
}
public void actionPerformed(ActionEvent e) {
counter--;
if (counter >= 1) {
call.setTimerLabel(counter);
} else {
timerLabel.setText("END");
timer.stop();
Toolkit.getDefaultToolkit().beep();
}
}
}
public static void main(String args[]) {
TimerClass gui = new TimerClass();
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.setSize(250, 150);
gui.setTitle("Time Setup");
gui.setVisible(true);
}
}
And now the ScoreBoard Screen
public class MatchScreen extends javax.swing.JFrame {
int redScore = 0, blueScore = 0, blueCat1 = 0,
blueCat2 = 0, redCat1 = 0, redCat2 = 0, winner = 0;
public MatchScreen() {
initComponents();
}
//Determine Winner of the match
public int getWinner() {
if (redScore > blueScore) {
winner = 1;
} else {
winner = 2;
}
return winner;
}
public void setTimerLabel(int a) {
int time = a;
while (time >= 1) {
timerLabel.setText("" + time);
}
if (time < 1) {
timerLabel.setText("End");
}
}
private void jButton13ActionPerformed(java.awt.event.ActionEvent evt) {
//Creates an object of the timerClass
TimerClass gui = new TimerClass();
gui.setSize(300, 175);
gui.setTitle("Time Setup");
gui.setVisible(true);
}
}
Some code that I felt is irrelevant was left out from the MatchScreen().
Many thanks
Managed to solve the general problem. I put all the code into one class. Not ideal, but it works :/ Anyway, deadlines are looming.
Sincere thanks.
You have a while loop in the setTimerLabel method, which I don't think you intended to put there. Also, you take the parameter a and assign it to time and then never use a again, why not just rename your parameter to time and bypass that additional variable?
EDIT
Sorry, I forgot to explain what I'm seeing :P If you say call.setTimerLabel(10) then you hit that while loop (while(time >= 1) which is essentially running while(10 >= 1) which is an infinite loop. Your program is never leaving the method setTimerLabel the first time you call it with a value >= 1.

Categories