I have a JFrame with two buttons. One of the buttons when clicked moves (btnMove) the other button(shape) from the present position to another.I am using a thread as a timer to count in seconds but each time the counter increments, the button moves back to its original position.
public class FrameTh extends JFrame {
class count extends Thread {
public int p = 0;
public void run() {
for (int i = 1; i < 100; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
lblCounter.setText("Seconds: " + i);
}
}
}
count t1 = new count();
private void formWindowActivated(java.awt.event.WindowEvent evt) {
t1.start();
}
private void btnMoveActionPerformed(java.awt.event.ActionEvent evt) {
shape.setLocation(23, 44);
}
The core problem is you're fighting the layout management API, which when you call setText is causing the container to be invalidated and relayed out
You might consider using something like JLayeredPane, but remember, you become entirely responsible for the size and position of the component
The other problem you have is you're violating the single threaded nature of Swing, Swing is not thread safe, meaning you shouldn't update the ui from out of the Event Dispatching Thread.
To solve that particular problem you should use a Swing Timer instead of a thread, see How to use Swing Timers for more details
Related
Alright, so I have a null layout JPanel with a single JLabel in it. The JLabel is positioned at (0,0). What I'm trying to do is use a while loop in a new Thread to sleep the new Thread and then shift the JLabel 10px down by using SwingUtilities.invokeLater . The problem is that the UI gets updated in a laggy sort of way. It doesn't update every time it should, but skips lots of updates and shifts in big chunks. I know I can easily do it with Timer, but the point is understanding Threads better. Thanks in advance!
Code:
private void start(){
Updater up = new Updater();
up.start();
}
public void updatePosition(){
int y = label1.getLocation.y;
label.setBounds(0,y+10, 10,10);
}
private class Updater extends Thread{
public void run(){
while(!shouldQuit){
try{
Updater.sleep(100);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updatePosition();
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
}
EDIT:
I got it to work by replacing the while loop with a call to a new Thread instance in the updatePosition() method, to settle things down a bit. And also, it wasn't only the Thread that was causing the problem, so I had to force the panel to re-layout it's subviews by calling revalidate() on it.
Here's how it looks (the fully working one):
private void start(){
new Updater().start();
}
public void updatePosition(){
int y = label1.getLocation.y;
label.setBounds(0,y+10, 10,10);
panel.revalidate();
if(!shouldQuit) new Updater().start();
}
private class Updater extends Thread{
public void run(){
try{
Updater.sleep(100);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updatePosition();
}
});
}catch(Exception e){
System.out.println(e);
}
}
}
You should try to use visibility and GridLayout to maximize movement. You can use a int var to count threads and reciprocate that to the label. As well, you should be using your ability o create Updaters, more and smoother. Just do the start() mwthod while trolling for threads :-)
You could have something besides an infinity call to start. I think you've lost the inheritance from the class, itself. The object label1 must ave been lost in tbe fray. If that's not it, then I'm pretty sure I'm not really able to answer this one.
I want to make a game that loops through an array every 1 second and then when the 'x' key is pressed the array stops printing, and the next 'x' key press the array resumes from the current element and loops again. my game starts with x and if i press again it just prints every 1 second. how do i add functionality and keep track of cards in the deck
My program is in java frame
public class Game {
private JFrame frame;
public static String[] cards = { "Jack", "Queen", "King" };
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Menu window = new Menu();
Timer timer = new Timer();
window.frame.setVisible(true);
//timer for card loops
timer.schedule(new GoCards(cards), 0, 1000);
window.frame.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e)
{
char key = e.getKeyChar();
if(key == 'x')
{
//pause the timer and if i press again then resume timer
timer.cancel();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
class GoCards extends TimerTask
{
String[] deck;
public GoCards(String[] cards)
{
deck = cards;
}
public void run()
{
for(int i = 0; i < 3; i++)
{
Thread.sleep(1000);
System.out.println(deck[i]);
}
}
Yours is a Swing application, so no, don't use Thread.sleep(...), not unless you want to risk putting your entire GUI to sleep, and don't use a java.util.Timer, since this does not work well with the Swing event thread. Instead you will want to use the tool best suited for the job -- a Swing Timer, aka javax.swing.Timer. The Timer can be started by calling start() on it and stopped simply by calling stop(), and you set the delay in milliseconds in its constructor. Also, use of a KeyListener is risky as it won't work if anything, such as a JButton for instance, steals the focus. Better to use Key Bindings. You can find links to the Swing tutorials and other Swing resources here: Swing Info.
You can make use of a timer such as wait
Thread.sleep(milliseconds);
Then keep track of each key press / iteration with a new counter n.
Checking for key presses would be a matter of simply handling them as they come in, or something as arbitrary as checking each iteration for any particular key.
So my JProgressBar I have set up doesn't work the way I want it. So whenever I run the program it just goes from 0 to 100 instantly. I tried using a ProgressMonitor, a Task, and tried a SwingWorker but nothing I tried works.
Here is my program:
int max = 10;
for (int i = 0; i <= max; i++) {
final int progress = (int)Math.round(
100.0 * ((double)i / (double)max)
);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
jProgressBar2.setValue(progress);
}
});
}
#MadProgrammer Here is my attempt at making a swing worker and writing each name to the document and updating the progress bar. The program gets to around 86 percent and stops, never creating the finished document. The program creates a blank document. Here are the two methods first is the SwingWorker object I made:
public class GreenWorker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
int max = greenList.size();
XWPFParagraph tmpParagraph;
XWPFRun tmpRun;
FileInputStream file =
new FileInputStream(location + "GreenBandList.docx");
gDocument = new XWPFDocument(OPCPackage.open(file));
for (int i = 0; i < max; i++) {
tmpParagraph = gDocument.getParagraphs().get(0);
tmpRun = tmpParagraph.createRun();
if (greenList.get(i).length() == 1) {
tmpRun.setBold(true);
tmpRun.setText(greenList.get(i));
tmpRun.setBold(false);
} else {
tmpRun.setText(greenList.get(i));//Write the names to the Word Doc
}
int progress = Math.round(((float) i / max) * 100f);
setProgress(progress);
}
return null;
}
}
And here is the code for the button that starts it and has my property change event.
private void GenerateGreenList() throws IOException, InterruptedException {
//Need to fix the bug that removes the Letter Header in Yellow Band list
//********************************************************************\\
//Delete the old list and make a new one
File templateFile = new File(location + "\\backup\\GreenTemplate.docx");
FileUtils.deleteQuietly(new File(location + "GreenBandList.docx"));
FileUtils.copyFile(templateFile, new File(location +
"GreenBandList.docx"));
//Get the New Entries
String[] entries = jTextPane3.getText().split("\n");
for (String s : entries) {
if (s != null) {
greenList.add(s);
}
}
//Resort the list
Collections.sort(greenList);
//Write the names to the document
GreenWorker worker = new GreenWorker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
jProgressBar2.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
if (worker.isDone()) {
try {
gDocument.write(new FileOutputStream(new File(location + "GreenBandList.docx")));
////////////////////////////////////////////////////////////
} catch (IOException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
JOptionPane.showMessageDialog(null, "Green Band List Created!");
jProgressBar2.setValue(0);
}
}
I used the property change listener from one of your other posts but I don't really understand what the one you wrote does or what it does in general?
Swing is a single threaded environment, that is, there is a single thread which is responsible for processing all the events that occur within the system, including repaint events. Should anything block this thread for any reason, it will prevent Swing from processing any new events, including, repaint events...
So all this ...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex); }
jProgressBar2.setValue(progress);
}
});
Is constantly pausing the Event Dispatching Thread, preventing it from actually doing any updates (or at least spacing them randomly)...
It's also likely that your outer loop is been run from within the context of the EDT, meaning that until it exists, nothing in the Event Queue will be processed. All your repaint requests will be consolidated down to a single paint request and voila, instant filled progress bar...
You really should use a SwingWorker - I know you said you tried one, but you've not shown any code as to your attempt in this regards, so it's difficult to know why it didn't work, however...
SwingWorker and JProgressBar example
SwingWorker and JProgressBar example
SwingWorker and JProgressBar example
SwingWorker and JProgressBar example
SwingWorker and dual welding JProgressBar example
SwingWorker and JProgressBar example
And forgive me if we haven't said this a few times before :P
You are evoking Thread.sleep inside the EvokeLater which means that it is running on another thread than your for loop. i.e., your for loop is completing instantaneously (well, however long it takes to loop from 1 to 100, which is almost instantaneously).
Move Thread.sleep outside of EvokeLater and it should work as you intend.
int max = 10;
for (int i = 0; i <= max; i++) {
final int progress = (int)Math.round(
100.0 * ((double)i / (double)max)
);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
jProgressBar2.setValue(progress);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex);
}
}
Edit: agree with #MadProgrammer. It appears this is just an illustrative question, but you should make sure whatever you're trying to accomplish here you use a SwingWorker for.
I have one three rectangles in my canvas. I wanted to change the colours of three rectangles
in a slow manner one by one.
For example: When starting the application, user should be able to see three rectangles with the same colour (blue).
After 2 secons that rectangles colour should change to red.
Again after 2 secons the next rectangles colour should get changed.
The last one is also done the same way, that means after 2 seconds of the 2nd rectangle.
I wrote in my own way. But it is not working. All the rectanlges are changed together. I want one by one.
Could anyone give me the logic.
final Runnable timer = new Runnable() {
public void run() {
//list of rectangles size =3; each contain Rectangle.
for(int i = 0 ; i < rectangleList.size();i++){
if(rectangleListt.get(i).getBackgroundColor().equals(ColorConstants.blue)){
try {
rectangleList.get(i).setBackgroundColor(ColorConstants.yellow);
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//rectSubFigureList.get(i).setBorder(null);
}/*else{
rectSubFigureList.get(i).setBackgroundColor(ColorConstants.blue);
}*/
}
You're likely calling Thread.sleep inside of Swing's event thread or EDT (for event dispatch thread), and this will cause the thread itself to sleep. Since this thread is responsible for all of Swing's graphics and user interactions, this will in effect put your entire application to sleep, and is not what you want to have happen. Instead, read up on and use a Swing Timer for this.
References:
Swing Timer tutorial
Swing Event Dispatch Thread and Swingworker tutorial
To expand on Hidde's code, you could do:
// the timer:
Timer t = new Timer(2000, new ActionListener() {
private int changed = 0; // better to keep this private and in the class
#Override
public void actionPerformed(ActionEvent e) {
if (changed < rectangleList.size()) {
rectangleList.setBackgroundColor(someColor);
} else {
((Timer) e.getSource()).stop();
}
changed++;
}
});
t.start();
You can set a Timer:
// declaration:
static int changed = 0;
// the timer:
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the colour here:
if (changed == 0) {
// change the first one
} else if (changed == 1) {
// change the second one
} else if (changed == 2) {
// change the last one
} else {
((Timer) e.getSource()).stop();
}
changed ++;
}
});
t.start();
How can I update the JProgressBar.setValue(int) from another thread?
My secondary goal is do it in the least amount of classes possible.
Here is the code I have right now:
// Part of the main class....
pp.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
new Thread(new Task(sd.getValue())).start();
}
});
public class Task implements Runnable {
int val;
public Task(int value){
this.val = value;
}
#Override
public void run() {
for (int i = 0; i <= value; i++){ // Progressively increment variable i
pbar.setValue(i); // Set value
pbar.repaint(); // Refresh graphics
try{Thread.sleep(50);} // Sleep 50 milliseconds
catch (InterruptedException err){}
}
}
}
pp is a JButton and starts the new thread when the JButton is clicked.
pbar is the JProgressBar object from the Main class.
How can I update its value?(progress)
The code above in run() cannot see the pbar.
Always obey swing's rule
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
What you can do is to create an observer that will update your progress bar -such as
- in this instance you want to show progress of data being loaded on click of a button.
DemoHelper class implements Observable and sends updates to all observers on when certain percent of data is loaded.
Progress bar is updated via public void update(Observable o, Object arg) {
class PopulateAction implements ActionListener, Observer {
JTable tableToRefresh;
JProgressBar progressBar;
JButton sourceButton;
DemoHelper helper;
public PopulateAction(JTable tableToRefresh, JProgressBar progressBarToUpdate) {
this.tableToRefresh = tableToRefresh;
this.progressBar = progressBarToUpdate;
}
public void actionPerformed(ActionEvent e) {
helper = DemoHelper.getDemoHelper();
helper.addObserver(this);
sourceButton = ((JButton) e.getSource());
sourceButton.setEnabled(false);
helper.insertData();
}
public void update(Observable o, Object arg) {
progressBar.setValue(helper.getPercentage());
}
}
Shameless plug: this is from source from my demo project
Feel free to browse for more details.
You shouldn't do any Swing stuff outside of the event dispatch thread. To access this, you need to create a Runnable with your code in run, and then pass that off to SwingUtilities.invokeNow() or SwingUtilities.invokeLater(). The problem is that we need a delay in your JProgressBar checking to avoid jamming up the Swing thread. To do this, we'll need a Timer which will call invokeNow or later in its own Runnable. Have a look at http://www.javapractices.com/topic/TopicAction.do?Id=160 for more details.
There is need not to call pbra.repaint explicitly.
Update JProgressBar shall be done through GUI dispatch thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Remember to make pbar final variable.
pbar.setValue(i);
}
});