I'm currently working on the following problem : How to use a thread in order to fill a progress bar (step by step) when the start button is clicked.
I encountered no problems building the thread or the progress bar.
However, when it came to filling the bar I proceeded as following :
public class event implements ActionListener{
#Override
public void actionPerformed(ActionEvent ae) {
Try t = new Try(250);
Thread MyT = new Thread(t);
MyT.start();
try {
MyT.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Gui.class.getName()).log(Level.SEVERE, null, ex);
}
pr.setValue(pr.getValue()+25);
System.out.println("check");
try {
MyT.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(Gui.class.getName()).log(Level.SEVERE, null, ex);
}
pr.setValue(pr.getValue()+25);
System.out.println("check");
}
}
The problem here is that I expected the bar to be filled with 25 units after 500 ms, then again filled with other 25 units(step by step) after 5000ms. Instead my program won't fill the bar with 25 after 500 ms, but with 50 after 5000 ms. Why is that and how can I make the bar charge gradually, without modifying the thread class ?
Yes, I know the question is old but maybe it helps someone.
I have implemented a progress bar class that provides a simple method, which fills the progress bar to a certain value in a specified amount of time.
Use like this:
// create progress bar
ProgressBar progressbar = new ProgressBar();
// fill progress bar in steps each one second
progressbar.fillInMills(1000, 25);
progressbar.fillInMills(1000, 50);
progressbar.fillInMills(1000, 75);
progressbar.fillInMills(1000, 100);
// resets to zero
progressbar.reset();
import javax.swing.JProgressBar;
public class ProgressBar extends JProgressBar {
Thread thread;
final int MAX = 100;
final int MIN = 0;
public ProgressBar() {
super(HORIZONTAL);
setMinimum(MIN);
setMaximum(MAX);
}
/**
* Fills the progress bar to the specified level in the specified time.
*
* #param millis the time in milliseconds
* #param fillTo the level in percentage
*/
public void fillInMills(final int millis, final int fillTo) {
thread = new Thread(() -> {
try {
final int startAt = getValue();
final int timeBetweenSteps = millis / Math.max(fillTo - startAt, 1);
for (int i = startAt; i <= fillTo; i++) {
setValue(i);
Thread.sleep(timeBetweenSteps);
}
} catch (final InterruptedException ignored) {
// thread is cancelled
}
});
thread.start();
}
/**
* Resets the ProgressBars level to zero.
*/
public void reset() {
if (thread != null) {
thread.interrupt();
}
this.setValue(0);
}
}
If you need advanced features of the JProgressBar, see this Oracle Tutorial on Progress Bars for detailed info.
Related
I'm a beginner in java so sorry if I'm asking a stupid question , but how do I make a new thread in my gui class that would create a progress bar. I have a class named progress and made a new thread in my gui class using the constructor that I have created. But for some reason, I am getting a strange error:
"constructor progress in class NewJFrame.progress cannot be applied to given types;
required: no arguments
found: JProgressBar
reason: actual and formal argument lists differ in length
NewJframe.java
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
if (jRadioButton1.isSelected()){
App m = new App();
Thread t1 = new Thread(new progress(jProgressBar1));
m.sendPingRequest2("104.160.142.3",jTextPane1,jTextPane2,jTextField1);
}
}
progress.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author User
*/
import javax.swing.JProgressBar;
public class progress implements Runnable {
private static int DELAY = 500;
JProgressBar progressBar;
public progress (JProgressBar bar) {
progressBar = bar;
}
public void run() {
int minimum = progressBar.getMinimum();
int maximum = progressBar.getMaximum();
for (int i = minimum; i < maximum; i++) {
try {
int value = progressBar.getValue();
progressBar.setValue(value + 1);
Thread.sleep(DELAY);
} catch (InterruptedException ignoredException) {
}
}
}
}
This:
Thread t1= new progress ( jProgressBar1);
Should be:
Thread t1 = new Thread(new progress(jProgressBar1));
since your progress class implements Runnable and does not extend Thread.
Also your error message is strange:
constructor progress in class NewJFrame.progress cannot be applied to given types
suggesting that the problem resides within the constructor of the NewJFrame.progress class, a class that looks to be nested within the NewJFrame class. If this is so, get rid of the nested class and only deal with the free-standing progress (re-name it "Progress" please) class.
But having said that, your code has potential problems as you're changing the state of the JProgressBar, a Swing component, directly from within a background thread, and this is not Swing thread-safe. Much better to use a SwingWorker and link it to the JProgressBar's state as per the JProgressBar standard tutorial (check the link please).
For example:
import java.awt.event.KeyEvent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
#SuppressWarnings("serial")
public class ProgressFun extends JPanel {
private JProgressBar progressBar = new JProgressBar(0, 100);
public ProgressFun() {
progressBar.setStringPainted(true);
final JButton startProgress = new JButton("Start Progress");
startProgress.setMnemonic(KeyEvent.VK_S);
startProgress.addActionListener(l -> {
startProgress.setEnabled(false);
progressBar.setValue(0);
final MyWorker myWorker = new MyWorker();
myWorker.execute();
myWorker.addPropertyChangeListener(pcEvent -> {
if (pcEvent.getPropertyName().equals("progress")) {
int value = (int) pcEvent.getNewValue();
progressBar.setValue(value);
} else if (pcEvent.getNewValue() == SwingWorker.StateValue.DONE) {
startProgress.setEnabled(true);
try {
myWorker.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
});
});
add(progressBar);
add(startProgress);
}
private static void createAndShowGui() {
ProgressFun mainPanel = new ProgressFun();
JFrame frame = new JFrame("Progress Fun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MyWorker extends SwingWorker<Void, Integer> {
#Override
protected Void doInBackground() throws Exception {
int progress = 0;
setProgress(progress);
while (progress < 100) {
progress += (int)(5 * Math.random());
progress = Math.min(progress, 100);
TimeUnit.MILLISECONDS.sleep((int) (500 * Math.random()));
setProgress(progress);
}
return null;
}
}
As an aside, 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.
the main problem is that I don't know how long my method will take to finish, I have 2 threads, one to execute the method that do stuff and the other one excute the progress of the progress bar, this is my first thread:
#Override
public void run() {
//This is a static variable, indicate when the method analyzeSentence finish
finish = false;
String sentence = analyzeSentence();
try {
Thread.sleep( 1000 );
} catch (InterruptedException e){
System.err.println( e.getMessage() );
}
finish = true;
}
And in my second thread (the progress bar thread) I have this:
#Override
public void run() {
i = 1;
while(!Analyze.finish) {
i = (i > 100) ? 1 : i+1; //Here I have the problem
progressBar.setValue(i);
progressBar.repaint();
try {
Thread.sleep(this.value);
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}
if(Analyze.finish) {
progressBar.setValue(100);
break;
}
}
}
The problem is that the progress bar reset their values to zero but the method still not over so the progress bar is filled again and again... Is there any way to obtain the lenght of the time that the method takes to finish?
First of all, apologies for how long winded this is.
I'm trying to make a simple roulette game that allows a user to add players, place bets for these players, and spin the roulette wheel, which is represented as a simple JLabel that updates it's text with each number it passes.
However, I've run into a bug that I'm having a lot of trouble with: the JLabel only updates the text for the last element in my loop.
Basically, my solution works like this:
When a user presses a button labelled "Spin" (given that users have been added to the game), I call a method from a class called SpinWheelService, which is an Observable singleton which in turn calls the notifyObservers() method:
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
String description = null;
if (ADD_PLAYER.equals(cmd)) {
addDialog();
} else if (PLACE_BET.equals(cmd)) {
betDialog();
} else if (SPIN.equals(cmd)) {
SpinWheelService.sws.setSpinWheelService();
} else if (DISPLAY.equals(cmd)) {
System.out.println("Display selected!");
}
}
Here is my SpinWheelService class:
package model;
import java.util.*;
public class SpinWheelService extends Observable {
public static SpinWheelService sws = new SpinWheelService();
public void setSpinWheelService() {
setChanged();
notifyObservers();
}
}
The only listener registered for SpinWheelService is this class, where GameEngine is my game engine that handles internal game logic, WheelCallbackImpl is a class that updates the View:
class SpinWheelObserver implements Observer {
GameEngine gameEngine;
ArrayList<SimplePlayer> players;
WheelCallbackImpl wheelCall;
int n;
public SpinWheelObserver(GameEngine engine, WheelCallbackImpl wheel, ArrayList<SimplePlayer> playerList) {
players = playerList;
gameEngine = engine;
wheelCall = wheel;
}
public void update(Observable sender, Object arg) {
// check if any players are present
if (players.size() == 0) {
System.out.println("Empty player array!");
return;
}
do {
gameEngine.spin(40, 1, 300, 30, wheelCall);
n = wheelCall.playback();
} while (n== 0);
}
}
The main point of note here is my gameEngine.spin() method, which is this:
public class GameEngineImpl implements GameEngine {
private List<Player> playerList = new ArrayList<Player>();
// method handles the slowing down of the roulette wheel, printing numbers at an incremental delay
public void delay(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
System.out.println("Sleep method failed.");
}
}
public void spin(int wheelSize, int initialDelay, int finalDelay,
int delayIncrement, WheelCallback callback) {
Random rand = new Random();
int curNo = rand.nextInt(wheelSize) + 1;
int finalNo = 0;
assert (curNo >= 1);
// while loop handles how long the wheel will spin for
while (initialDelay <= finalDelay) {
delay(initialDelay);
initialDelay += delayIncrement;
// handles rotating nature of the wheel, ensures that if it reaches wheel size, reverts to 1
if (curNo > wheelSize) {
curNo = 1;
callback.nextNumber(curNo, this);
curNo++;
}
assert (curNo <= wheelSize);
callback.nextNumber(curNo, this);
curNo++;
finalNo = curNo - 1;
}
calculateResult(finalNo);
callback.result(finalNo, this);
}
The method callback.nextNumber(curNo, this):
public void nextNumber(int nextNumber, GameEngine engine) {
String strNo = Integer.toString(nextNumber);
assert (nextNumber >= 1);
System.out.println(nextNumber);
wcWheel.setCounter(strNo);
}
Where in, wcWheel is my singleton instance of my View, which contains the method setCounter():
public void setCounter(String value) {
label.setText(value);
}
Sorry for how convoluted my explanation is, but basically what it boils down to is that setCounter() is definitely being called, but seems to only call the setText() method on the final number. So what I'm left with is an empty label that doesn't present the number until the entire roulette has finished spinning.
I've determined that setCounter() runs on the event dispatch thread, and I suspect this is a concurrency issue but I have no idea how to correct it.
I've tried to include all relevant code, but if I'm missing anything, please mention it and I'll post it up as well.
I'm at my wits end here, so if anyone would be kind of enough to help, that would be so great.
Thank you!
Your while loop along Thread.sleep() will block and repainting or changing of the UI until the loop is finished.
Instead you'll want to implement a javax.swing.Timer for the delay, and keep a counter for the number of ticks, to stop it. You can see more at How to Use Swing Timers
The basic construct is
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis is the millisecond delay between firing of an ActionEvent. This event is listened for by the listener. So every time the event is fired, the actionPerfomed of the listener is called. So you might do something like this:
Timer timer = new Timer(delay, new ActionListener()(
#Override
public void actionPerformed(ActionEvent e) {
if (count == 0) {
((Timer)e.getSource()).stop();
} else {
//make a change to your label
count--;
}
}
));
You can call timer.start() to start the timer. Every delay milliseconds, the label will change to what you need it to, until some arbitrary count reaches 0, then timer stops. You can then set the count variable to whatever you need to, if you want to to be random, say depending on how hard the wheel is spun :D
I think you didn't post all the relevant code that is required to know exactly the problem.
But most likely the problem is due to you run your loop and JLabel.setText() in the EDT (Event Dispatching Thread).
Note that updating the UI components (e.g. the text of a JLabel) also happens in the EDT, so while your loop runs in the EDT, the text will not be updated, only after your loop ended and you return from your event listener. Then since you modified the text of the JLabel it will be refreshed / repainted and you will see the last value you set to it.
Example to demonstrate this. In the following example a loop in the event listener loops from 0 to 9 and sets the text of the label, but you will only see the final 9 be set:
JPanel p = new JPanel();
final JLabel l = new JLabel("-1");
p.add(l);
JButton b = new JButton("Loop");
p.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for ( int i = 0; i < 10; i++ ) {
l.setText( "" + i );
try { Thread.sleep( 200 ); } catch ( InterruptedException e1 ) {}
}
}
} );
A proposed solution: Use javax.swing.Timer to do the loop's work. Swing's timer calls its listeners in the EDT so it's safe to update swing components in it, and once the listener returns, a component UI update can happen immediately:
JPanel p = new JPanel();
final JLabel l = new JLabel("-1");
p.add(l);
JButton b = new JButton("Loop");
p.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Timer(200, new ActionListener() {
int i = 0;
#Override
public void actionPerformed(ActionEvent e2) {
l.setText("" + i);
if ( ++i == 10 )
((Timer)e2.getSource()).stop();
}
}).start();
}
} );
In this solution you will see the label's text counting from 0 up to 9 nicely.
It's appears to me that your entire game must block in the action handler until the while loop has finished? So the text of the label will be getting updated but only the last update will be visible once the AWT thread is running again.
Anyone have any idea how to make check box do a task until it gets unchecked?
JCheckBox chckbxInspecAuto = new JCheckBox("Inspe. Auto.");
chckbxInspecAuto.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0)
{
try {
gimage = vision.grab();
cvSaveImage("image001.bmp", gimage);
ipanel.loadImage("image001.bmp");
} catch (com.googlecode.javacv.FrameGrabber.Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
EDIT: The code is over there now...
You could extend the SwingWorker class or even Thread to implement the functionality you want the checkbox to control. Create methods contiueWorking() and stopWorking() and in the run method check some boolean flag to see whether to do the task or sleep.
As Robin commented on a post below, swing components should only be accessed from the Event Worker Thread. I violated this rule in my example below because I wanted to produce something simple and since the running thread spends most of its time sleeping, it works fine.
I would use SwingWorker for a more time consuming task that I wish to update the user on as it executes. For instance, say we have a thread generate and then send a set of email messages, and for each e-mail, display some text in the UI that confirms the message was sent or indicates why it was undeliverable. Each message may lock the Thread up for a while. The UI will not be updated in sync with the Thread's execution of the e-mail task. SwingWorker to the rescue. The java2s.com site has a SwingWokerDemo.
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;
/**
* The sequence of prime integers is calculated and displayed on the screen.
* This little program demonstrates how a checkbox can be used
* to start and stop a thread.
*/
public class ThreadDemo extends JFrame {
JCheckBox runningCB;
JTextArea displayArea;
JTextField delayField;
PrimeCalcThread workerThread = null;
public ThreadDemo() {
super("Prime Numbers");
runningCB = new JCheckBox("Calculate Primes");
JPanel topPanel = new JPanel();
topPanel.add(runningCB);
topPanel.add(new JLabel("Dealy: "));
delayField = new JTextField(10);
delayField.setText("500");
topPanel.add(delayField);
getContentPane().add(topPanel,BorderLayout.NORTH);
displayArea = new JTextArea(30,80);
displayArea.setText("2, 3, 5, ");
displayArea.setLineWrap(true);
JScrollPane scroller = new JScrollPane(displayArea);
getContentPane().add(scroller,BorderLayout.CENTER);
pack();
runningCB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(runningCB.isSelected() ) {
if(workerThread != null)
workerThread.contiueWorking();
else {
workerThread = new PrimeCalcThread();
workerThread.start();
}
}
else {
if(workerThread != null)
workerThread.stopWorking();
}
}
});
delayField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String input = delayField.getText().trim();
try {
int d = Integer.parseInt(input);
workerThread.setDelay(d);
}
catch(NumberFormatException nfe) { }
}
});
setVisible(true);
}
public static void main(String[] arg) {
ThreadDemo window = new ThreadDemo();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* When this thread is active it calculates prime numbers. The Thread
* continues until it is paused with stopWorking() or until the
* boolean field endThread is set to true.
*/
class PrimeCalcThread extends Thread {
ArrayList<Integer> primeList;
int delay = 500;
boolean active = true;
boolean endThread = false;
private int lastPrime;
public PrimeCalcThread() {
primeList = new ArrayList<> (1024);
primeList.add(2); primeList.add(3); primeList.add(5);
lastPrime=5;
}
public void run() {
while(!endThread) {
if(active) {
calcNextPrime();
displayArea.append(lastPrime + ", ");
}
try { sleep(delay); }
catch(InterruptedException whatever) { }
}
}
private void calcNextPrime() {
int p = lastPrime+2;
while(!isPrime(p))
p+=2;
primeList.add(p);
lastPrime = p;
}
/**
* Checks if p is prime by checking for divisibility by all primes in the
* calculated primeList so far. This method only works if sqrt(p) < lastPrime
*/
private boolean isPrime(int p) {
int maxCheck = (int) Math.sqrt(p) + 1;
for(int prime: primeList) {
if(p % prime == 0)
return false;
if(prime > maxCheck)
break;
}
return true;
}
public int getLastPrime() {
return lastPrime;
}
public ArrayList<Integer> getPrimeList() {
return primeList;
}
public int getDelay() {
return delay;
}
public void setDelay(int waitTime) {
if(waitTime>=0)
delay = waitTime;
}
public void contiueWorking() {
active=true;
}
public void stopWorking() {
active=false;
}
}
}
- Well create a Daemon Thread and run it in an infinite loop, let it keep checking whether the JCheckBox is checked or not using isSelected() method or ItemListener.
- And when the JCheckBox gets selected then create a Thread to execute the task, or use SwingWorker Class which properly synchronizes the UI and Non-UI thread's work.
It depends on the task.
If a task is on other process on other machine you could simply send the right messages.
If the task is a separated thread in the same application you could do what wxyz suggests or you could use some kind of Listener with the ability of start and stop the Thread (the thread is always the same or you are creating a new one every time you select the checkbox?).
My favorite one would be to use Observer pattern in the "PUSH" way, so you would use some kind of external signal to the thread which when sent would create and/or stop it.
i really need to find better ways to word my questions.
Basically I've created a program that takes information from a webpage and displays it nicely across the screen.
When the user closes the program, they actually hide it.
I also have another method which constantly loops checking for information to see if tis been updated.
unfortunately the problem im having is that it loops to fast, i only want it to check for information every 40 seconds or so.
What i tried was inserting a wait(1000,1000) in the method itself and in the main of the program. but both of these cause IllegalMonitorStateException.
Is this the correct way to make the thread wait properly? or is there a better way?
note: the only thread i have is the main.
MAIN
class Marquee
{
public static void main(String[] args) throws InterruptedException
{
MyFrame frame = new MyFrame();
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
frame.setAlwaysOnTop(true);
frame.setBackground(Color.BLACK);
frame.setResizable(true);
while(true)
{
// this doesnt work
frame.wait(1000,1000);
frame.notifyAll();
frame.checkForNewUpdate();
System.out.println(" ____________________________next line _______________________________");
}
}
}
CHECK FOR UPDATES
public String[] checkForNewUpdate()
{
//setVisible(true);
String tempUpdate = getEngineersUpdate();
if (latestUpdate[0] != tempUpdate)
{
// do nothign
setVisible(false);
}
else if(latestUpdate[0]==tempUpdate)
{
latestUpdate[0] = tempUpdate;
//show the page again
setVisible(true);
}
else if(latestUpdate[0]!= "NULL")
{
// do nothing
//latestUpdate[0] = tempUpdate;
}
else
{
latestUpdate[0] = tempUpdate;
}
return latestUpdate;
}
1: WHat am i doing wrong to get this exception
2: Is there any other way to make a gap of time in a method
3: Am i going to have to put all these methods into another thread? Please say no
// my constructor which I failed to mention has a timer in it. only i dont know hwo to use it
class MyFrame extends JFrame implements ActionListener
{
private ActionListener listener;
private Timer t1;
private String [] latestUpdate = new String[1];
public MyFrame()
{
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();// gets the maximum size of the screen
setSize(d.width,(d.height/100)*10);//sets it to max. need to change this
// this shit find the max size of screen and puts it bottom left
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice defaultScreen = ge.getDefaultScreenDevice();
Rectangle rect = defaultScreen.getDefaultConfiguration().getBounds();
int x = (int)rect.getMinX();
int y = (int)rect.getMaxY()-getHeight();
setLocation(x,y-30);
setTitle("ALERT::OUTAGE");
MyPanel panel = new MyPanel();
add(panel);
listener = this;
t1 = new Timer(50,listener);
t1.start();
}
by request, here is getEngineersUpdate()
public String getEngineersUpdate() //gets data from page and sets it to string.
{
String update = "blank";
final WebClient webClient = new WebClient();
webClient.setJavaScriptEnabled(false);// javascript causes some serious problems.
webClient.setCssEnabled(false);
String forChecking;
HtmlPage page;
try
{
URL outageURL = new URL("file:\\C:\\Users\\0vertone\\Desktop\\version control\\OUTAGE\\Outages.html"); //local drive at home
page = webClient.getPage(outageURL);
//All this crap can be gone if we just give the table an id
Object[] dates = page.getByXPath("//span[#id='date']/text()").toArray();
Object[] sites = page.getByXPath("//span[#id='site']/text()").toArray();
Object[] issues = page.getByXPath("//span[#id='issue']/text()").toArray();
System.out.println("" + dates[0].toString());
System.out.println("" + sites[0].toString());
System.out.println("" + issues[0].toString());
update = (dates[0].toString() + " " + sites[0].toString() + " " +issues[0].toString());
forChecking = dates[0].toString();
/**some examples of the getCellAt() method*/
//update = table.getCellAt(0,0).asText(); // This returns DATE/Time
//update = table.getCellAt(1,0).asText(); // This return the actual date
//update = table.getCellAt(0,1).asText(); // This returns, SITE/Sector
//update = table.getCellAt(1,1).asText(); // This returns the actual site issue
}
catch (FailingHttpStatusCodeException a)
{
System.out.println("Failing HTTP Status Execution");
a.printStackTrace();
}
catch (MalformedURLException b)
{
System.out.println("Malformed URL");
b.printStackTrace();
}
catch (IOException c)
{
System.out.println("IO PROBLEMS!");
c.printStackTrace();
}
webClient.closeAllWindows();
return update;
}
I've changed your code so it should work as you intended. I'm not clear on what getEngineersUpdate() does, so I can't say for sure if it will work, but I've given you a start. I've included 2 options for how to handle it, with explanation in the comments. You can see how to use a Timer properly in the constructor, also. Finally, I don't have your full code, so I had to rig something together to simulate it.
class Marquee {
public static void main(String[] args) throws InterruptedException {
MyFrame frame = new MyFrame();
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
frame.setAlwaysOnTop(true);
frame.setBackground(Color.BLACK);
frame.setResizable(true);
}
}
class MyFrame extends JFrame {
private String [] latestUpdate = new String[1];
private static final int DISPLAY_TIME = 3000;
private Timer displayTimer;
/*
* Option #1:
* Ideally, you'd have the thread that generates the "Engineers Update" messages call this
* method. If you can't make this event based, then you should use option #2
*/
public void newUpdate(String message) {
setVisible(true);
// change this to whatever you need to.
text.setText(message);
displayTimer.restart();
}
// I used this to test it
private JTextField text;
public MyFrame() {
// gets the maximum size of the screen
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
//sets it to max. need to change this
setSize(d.width, (d.height / 100) * 10);
// this shit find the max size of screen and puts it bottom left
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice defaultScreen = ge.getDefaultScreenDevice();
Rectangle rect = defaultScreen.getDefaultConfiguration().getBounds();
int x = (int) rect.getMinX();
int y = (int) rect.getMaxY() - getHeight();
setLocation(x, y - 30);
setTitle("ALERT::OUTAGE");
//MyPanel panel = new MyPanel();
//add(panel);
text = new JTextField("Initial Text");
add(text);
// this creates a timer that when it goes off, will hide the frame
displayTimer = new Timer(DISPLAY_TIME, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
// sets the timer not to repeat
displayTimer.setRepeats(false);
//This code is for option #2:
updateTimer = new Timer(UPDATE_INTERVAL, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
checkForNewUpdate();
}
});
updateTimer.start();
}
// This is for option #2
private static final int UPDATE_INTERVAL = 1000;
private Timer updateTimer;
/*
* Option #2:
* Not ideal, but this should work.
*/
public String[] checkForNewUpdate() {
// I don't know how getEngineersUpdate() works
// which would have made it much easier to help you.
String tempUpdate = getEngineersUpdate();
// String comparison doesn't work like this in java.
// you also had a sleeping NullPointerException here
if (!tempUpdate.equals(latestUpdate[0])) {
// this is when you have a new update, correct?
newUpdate(tempUpdate);
latestUpdate[0] = tempUpdate;
} else if (tempUpdate.equals(latestUpdate[0])) {
// it's the same update as last time, so do nothing
} else if (tempUpdate.equals("NULL")) {
// You need to handle this according to what getEngineersUpdate() does
}
return latestUpdate;
}
// This code is rigged to show how it would work
private static int i = 0;
private String getEngineersUpdate() {
// 1 in 6 chance of returning "NULL"
if (Math.random() * 6 - 1 < 0)
return "NULL";
// probability of 1 in 4 of generating a new update
if(Math.random() * 4 - 1 < 0)
return "UPDATE #"+i++;
else
return "UPDATE #"+i;
}
}
I think you can't call wait() on an JFrame, but I am not sure.
You have to call wait() within a snychronized-block. (Example below)
Thread.sleep(1000l) can be used, if it runs in a Thread, but look for the class Timer
It would be much better design, if you create a thread, which checks for updates. You can notify the GUI (JFrame) with some kind of event-listener about the new date to display.
Take a look at the Timer and Callable.
You should create another thread, you should call checkforNewUpdate method from this thread. And also do not forget use SwingUtilities.invokeLater method to update your UI inside checkforNewUpdate method. here is the some part of the code;
public class Marque {
private JFrame frame;
class CheckForUpdate implements Runnable {
public void run() {
while(true) {
checkForNewUpdate();
try {
Thread.sleep(40000);
} catch (InterruptedException e1) {
e1.printStackTrace();
throw new RuntimeException(e1);
} }
}
public String[] checkForNewUpdate() {
//your code
// user interface interaction code
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
}
}
public Marque() {
frame = new JFrame();
//....frame related code
new Thread(new CheckForUpdate()).start();
}
public static void main(String[] arg) {
Marque marque = new Marque();
}