Objective
I am trying to achieve code that will call upon a method for the time the JToggleButton in Swing is pressed down. The MainCode.intGenerate method, which returns an List<Integer>, takes in aList<Integer>.
Restrictions
Random_gen_Button is declared as such and is unchangeable due to NetBeans guarding it. I can change it using notepad to open but It is not allowing me to change it for a reason so I shall not.
private JToggleButton RANDOMGENBUTTON;
Code
private void RANDOMGENBUTTONActionPerformed(java.awt.event.ActionEvent evt) {
if(RANDOMGENBUTTON.isSelected()) {
EXITBUTTON.setEnabled(false);
List<Integer> INPUTLIST = new ArrayList<Integer>();
while(RANDOMGENBUTTON.isSelected()) {
INPUTLIST = MainCode.intGenerate(INPUTLIST);
}
}else {
EXITBUTTON.setEnabled(true);
}
}
Possible Solution
I think this should be achievable with another Thread but I am not sure how to do this.
The Error
This code does not work because it freezes the GUI, I cannot press on the JToggle Button again resulting in an infinite loop.
Edit
intGenerate()
public static List<Integer> intGenerate(List<Integer> INLIST) {
Random Rand = new Random();
int RANDOMNUM = Rand.nextInt((9999999 - 1000000) + 1) + 1000000;
if(INLIST.isEmpty()) {
INLIST.add(randomNum);
}else {
while(INLIST.contains(randomNum)==true) {
RANDOMNUM = Rand.nextInt((9999999 - 1000000) + 1) + 1000000;
}
INLIST.add(RANDOMNUM);
}
return INLIST;
Use a java.swing.Timer for the other thread that periodically tells your component to update itself. In the example below, the timer's listener updates the button's text when the button is selected or the button's model says it's pressed.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JToggleButton;
import javax.swing.Timer;
/**
* #see http://stackoverflow.com/a/38051563/230513
*/
public class Test {
private final Random r = new Random();
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JToggleButton button = new JToggleButton("0000");
f.add(button);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
Timer t = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (button.isSelected() | button.getModel().isPressed()) {
button.setText(String.valueOf(r.nextInt(9000) + 1000));
}
}
});
t.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Test()::display);
}
}
As an aside, don't let the GUI editor dictate your design.
Related
I'm currently stuck on a project at the moment component("c") cannot be resolved, I know I'm getting this issue as I'm trying to grab "c" from outside the class from the repaint method but have no idea how to get around this. As far as I'm concerned if I am able to grab "c" then my whole program would. Any help will be massively appreciated. thanks!
package adp.cwr2122;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class SomApplication extends JFrame {
private static final long serialVersionUID = 1L;
private final JProgressBar bar = new JProgressBar();
private final SoM som = new SoM(800,600);
public SomApplication() {
final JPanel mainPanel = new JPanel(new BorderLayout());
final JPanel southPanel = new JPanel(new BorderLayout());
final JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(ev -> doCancel());
this.bar.setMaximum(5000);
southPanel.add(this.bar);
southPanel.add(cancelButton, BorderLayout.EAST);
final SomComponent c = new SomComponent(this.som);
mainPanel.add(c);
mainPanel.add(southPanel, BorderLayout.SOUTH);
add(mainPanel);
pack();
setVisible(true);
this.som.initialise();
c.repaint();
final long start = System.currentTimeMillis();
while(SomApplication.this.som.range() > 0) {
SomApplication.this.som.doOne();
SomApplication.this.bar.setValue((int)((5000 * (SomApplication.this.som.maxRange() - SomApplication.this.som.range())) / SomApplication.this.som.maxRange()));
c.repaint();
}
final long elapsed = System.currentTimeMillis() - start;
System.out.println("Done " + elapsed);
}
private void doCancel() {
System.out.println( "Cancel button pressed");
}
private static class SomComponent extends JComponent {
private static final long serialVersionUID = 1L;
private final SoM som;
public SomComponent( final SoM som) {
this.som = som;
new Thread(new Runnable() {
public void run(){
queuePaint();
System.out.println("Thread is working");
}
}).start();
}
public void repaint() {
if(SwingUtilities.isEventDispatchThread()) {
c.repaint();
}
else {
SwingUtilities.invokeLater(c.repaint);
}
}
public void queuePaint() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println("Queue Paint is being called");
setPaint(); }
});
}
public void setPaint() {
System.out.println("Paint is being called");
paintComponent(this.getGraphics());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(this.som.image().getWidth(), this.som.image().getHeight());
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
g.drawImage(this.som.image(), 0, 0, this);
repaint();
}
}
public static void launch() {
new SomApplication();
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SomApplication();
}
});
}
}
As you have recognized yourself your problem is access to c in the repaint() method. You only declare c in the scope of the constructor of SomApplication, so everything outside that scope (read: outside the curly braces of the SomApplicaton constructor) has no idea what c is supposed to be, because it is not declared.
One way to resolve this is declaring c as a member of SomApplication. However you will run into an additional problem there. Your nested class is static, so if you declare c as a member of SomApplication, then SomComponent cannot be static in order to "see" c.
Having said all that there is something fishy with your code there. Because c is an instance of SomComponent and presumably you intend to update itself I then don't see why c is in repaint() to begin with. Your repaint() does not override the JComponent#repaint and your c is presumably supposed to be this.
It's possible I misunderstand something about your architecture, but are you sure you need the c at all in there? Why don't you call repaint() rather than c.repaint() in SomComponent?
If you took the code piece in your SomComponent#repaint method from some online example, take a look at whether you need that code at all. JComponent already comes with repaint(), so maybe this is just the result of a bad copy+paste. Not sure.
I have a problem with one of my frames not looking as it should, when it is called upon the press of a button.
The frame looks as if it was rendered improperly, the label text in it is shortened, however when i move the same line of code outside the action listener, it works as it should.
I have a sort of main menu, with two buttons, only the Generate Menu works at the moment, it looks like this:
https://i.imgur.com/k1Ne5v9.png
The code for the action listener:
runMenuButt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Generate Menu pressed");
mF.dispose();
MenuGenerator.generateTheMenu();
}
});
The result looks wrong: https://i.imgur.com/n86y4CD.png
The frame is also unresponsive, clikcing X does not, while it should close the frame and the application.
However changing the code to:
runMenuButt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Generate Menu pressed");
//mF.dispose();
}
});
MenuGenerator.generateTheMenu();
Produces correct look: https://i.imgur.com/TFbkmAO.png
The code for the "Main menu"
public static void openMainMenu() {
Font menuFont = new Font("Courier",Font.BOLD,16);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mF.setSize(465,230);
mF.setLocation(dim.width/2-mF.getSize().width/2, dim.height/2-mF.getSize().height/2);
mF.getContentPane().setBackground(Color.WHITE);
Color blueSteel = new Color(70,107,176);
JPanel p = new JPanel();
p.setSize(600,50);
p.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
p.setLocation((mF.getWidth() - p.getWidth()) /2, 20);
p.setBackground(blueSteel);
JLabel l = new JLabel("Welcome to the menu GENERATORRRR");
l.setFont(menuFont);
l.setForeground(Color.WHITE);
p.add(l, gbc);
JButton runMenuButt = new JButton("Generate Menu");
runMenuButt.setLocation(20 , 90);
JButton manageRecipButt = new JButton("Manage Recipients");
manageRecipButt.setLocation(240 , 90);
menuUtilities.formatButton(runMenuButt);
menuUtilities.formatButton(manageRecipButt);
mF.setResizable(false);
mF.setLayout(null);
mF.add(runMenuButt);
mF.add(manageRecipButt);
mF.add(p);
mF.setVisible(true);
runMenuButt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Generate Menu pressed");
//mF.dispose();
}
});
MenuGenerator.generateTheMenu();
manageRecipButt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Not supported yet", "Function not yet available",JOptionPane.INFORMATION_MESSAGE);
}
});
//System.out.println(mF.getContentPane().getSize());
}
And the status bar:
public class StatusBar {
private static JLabel statusLabel= new JLabel("Starting");
private static JFrame statusFrame = new JFrame("Generation Status");
public static void createStatusBar() {
Font menuFont = new Font(Font.MONOSPACED,Font.BOLD,20);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
statusFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
statusFrame.setSize(700,100);
JPanel p = new JPanel();
p.setPreferredSize(new Dimension(100,100));
statusLabel.setFont(menuFont);
p.add(statusLabel);
statusFrame.add(p,BorderLayout.CENTER);
statusFrame.setLocation(dim.width/2-statusFrame.getSize().width/2, dim.height/2-statusFrame.getSize().height/2);
statusFrame.setVisible(true);
}
public static void setStatusBar(String statusText) {
statusLabel.setText(statusText);
statusLabel.paintImmediately(statusLabel.getVisibleRect());
statusLabel.revalidate();
}
public static void closeStatusBar(){
statusFrame.dispose();
}
}
I create the bar with this line:
StatusBar.createStatusBar();
Why does the status bar not render properly when the MenuGenerator.generateTheMenu(); is called from the action listener?
Here is minimal code that reproduces this behavior for anyone who would like to test it: It also uses class for the StatusBar, which is already posted.
public class MinimalClass {
private static JFrame mF = new JFrame("Main Menu");
public static void main(String[] args) {
openMainMenu();
}
public static void openMainMenu() {
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mF.setSize(465,230);
mF.setLocation(dim.width/2-mF.getSize().width/2, dim.height/2-mF.getSize().height/2);
mF.getContentPane().setBackground(Color.WHITE);
JButton runMenuButt = new JButton("Generate Menu");
runMenuButt.setLocation(20 , 90);
runMenuButt.setSize(200 , 85);
mF.setResizable(false);
mF.setLayout(null);
mF.add(runMenuButt);
mF.setVisible(true);
runMenuButt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Generate Menu pressed");
mF.dispose();
generateTheMenu();
}
});
}
public static void generateTheMenu() {
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
String rawMenuOutput = "";
try {
rawMenuOutput= getMenuInJavaNow();
} catch (Exception e){
System.out.println("Something went terribly wrong");
}
System.out.println(rawMenuOutput);
}
public static String getMenuInJavaNow() throws IOException {
String rawMenuOutput = "Restaurant Menu" ;
rawMenuOutput = rawMenuOutput + "Test line";
String []menuOtpArr = new String [3];
try {
StatusBar.createStatusBar();
TimeUnit.SECONDS.sleep(2);
StatusBar.setStatusBar("Test1");
TimeUnit.SECONDS.sleep(2);
menuOtpArr[0]="Test line";
StatusBar.setStatusBar("Test2");
TimeUnit.SECONDS.sleep(2);
menuOtpArr[1]="Test line";
StatusBar.setStatusBar("Test3");
TimeUnit.SECONDS.sleep(2);
menuOtpArr[2]="Test line";
StatusBar.setStatusBar("Test4");
TimeUnit.SECONDS.sleep(2);
StatusBar.closeStatusBar();
} catch (Exception e) {
}
for (int i=0;i < menuOtpArr.length;i++) {
rawMenuOutput = rawMenuOutput + "\n\n" +menuOtpArr[i];
}
return rawMenuOutput;
}
}
Thank you for your time
statusLabel.paintImmediately(statusLabel.getVisibleRect()); seems to masking a larger issue.
The problem is, Swing is single threaded (and NOT thread safe). This means that when you call TimeUnit.SECONDS.sleep(2); from within getMenuInJavaNow, which is called by generateTheMenu, which is called by the ActionListener, it's been called within the context of the Event Dispatching Thread.
This is putting the EDT to sleep, meaning that it isn't processing layout or paint requests (properly)
Start by having a read of Concurrency in Swing for more details
Now, you have a larger issue, how to solve it. For the answer to that question, we require a lot more context then is currently available.
The getMenuInJavaNow seems to be returning some values, to what end I'm not sure.
"A" solution, would be to use a SwingWorker (see Worker Threads and SwingWorker for more details). It provides the ability to execute long running tasks in the background, but also provides the means for sync updates back to the UI, for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class MinimalClass {
private static JFrame mF = new JFrame("Main Menu");
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
openMainMenu();
}
});
}
public static void openMainMenu() {
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mF.setLocationRelativeTo(null);
mF.getContentPane().setBackground(Color.WHITE);
JButton runMenuButt = new JButton("Generate Menu");
runMenuButt.setMargin(new Insets(25, 25, 25, 25));
JPanel buttons = new JPanel(new GridLayout(0, 2));
buttons.add(runMenuButt);
mF.add(buttons);
mF.pack();
mF.setVisible(true);
runMenuButt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Generate Menu pressed");
mF.dispose();
generateTheMenu();
}
});
}
public static void generateTheMenu() {
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
StatusBar.createStatusBar();
SwingWorker<String, String> worker = new SwingWorker<String, String>() {
#Override
protected String doInBackground() throws Exception {
String rawMenuOutput = "Restaurant Menu";
rawMenuOutput = rawMenuOutput + "Test line";
String[] menuOtpArr = new String[3];
try {
TimeUnit.SECONDS.sleep(2);
publish("1234567890123456789012345678901234567890");
TimeUnit.SECONDS.sleep(2);
publish("This is a test");
TimeUnit.SECONDS.sleep(2);
publish("More testing");
TimeUnit.SECONDS.sleep(2);
publish("Still testing");
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
}
for (int i = 0; i < menuOtpArr.length; i++) {
rawMenuOutput = rawMenuOutput + "\n\n" + menuOtpArr[i];
}
return rawMenuOutput;
}
#Override
protected void done() {
StatusBar.closeStatusBar();
}
#Override
protected void process(List<String> chunks) {
StatusBar.setStatusBar(chunks.get(chunks.size() - 1));
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
try {
String result = worker.get();
System.out.println(result);
StatusBar.closeStatusBar();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
}
});
worker.execute();
}
public static class StatusBar {
private static JLabel statusLabel = new JLabel("Starting");
private static JFrame statusFrame = new JFrame("Generation Status");
public static void createStatusBar() {
Font menuFont = new Font(Font.MONOSPACED, Font.BOLD, 20);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
statusFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
statusFrame.setSize(700, 100);
JPanel p = new JPanel();
p.setBackground(Color.RED);
// p.setPreferredSize(new Dimension(100, 100));
statusLabel.setFont(menuFont);
p.add(statusLabel);
statusFrame.add(p, BorderLayout.CENTER);
statusFrame.setLocationRelativeTo(null);
statusFrame.setVisible(true);
}
public static void setStatusBar(String statusText) {
statusLabel.setText(statusText);
}
public static void closeStatusBar() {
statusFrame.dispose();
}
}
}
Observations...
static is not your friend, especially in cases like this. You really, really, really need to learn to live without it.
setLayout(null) is not doing you any favours, especially in the long run. Take the time to go through Laying Out Components Within a Container and start making proper use of layout managers, they might seem "complicated", but they will save you from a lot of hair loss
Avoid using setPreferred/Minimum/MaximumSize where ever possible, you are robing the component of the ability to provide useful rendering hints which may change across platforms and rendering pipelines
Just a quick follow up question, what is the difference between done and addPropertyListener ? Is there any? Isnt it redundant to use both?
The example here is pretty basic, for me I've used done to handle what the SwingWorker "knows" needs to be done, it doesn't however, know what is to be done with the result.
I've used the PropertyChangeListener to deal with that instead - the point - it's an example.
And I also noticed, that I dont need to actually publish, as calling StatusBar.setStatusBar(""); works as well. Is it necessary to use publish?
In a word YES. Swing is NOT thread safe, calling StatusBar.setStatusBar("") directly can lead to some weird and unexpected results. publish pushes the call into the Event Dispatching Thread, making it safe to update the UI from within.
I have the code for generating the String I want to set as the StatusBar Title in another class, not in the generateTheMenu, therefore it is more convenient for me to simply call .setStatusBar. The not minimal code I have is actually something like this
This is where things like interfaces come in really handy. You "string" generating class "could" either return the resulting text OR you could pass a reference to a interface implementation which is used to "display" it. This way, your SwingWorker could act as a consumer for the String and pass it through the publish method.
There are a number of really important concepts to understand.
You want to decouple your code. This makes it easier to change certain parts of the code without affecting the other parts
You want to be able to "code to interface, not implementation". This goes hand in hand with the first comment. Basically, you want to "hide" the implementation details as much as possible - lots of different reasons for it, but it helps keep your code lean, helps make the follow more understandable and stops one part of the code from accessing another it really has no responsibility to do so (is the string generation really responsible for updating the status bar? IMHO - not really)
There is also a swagger of design patterns available to make solving issues easier. I've already mentioned the concept of "produce/consumer", but this is just one
The "Event Dispatching Thread"
The Event Dispatch Thread
Java Event-Dispatching Thread explanation
Swing threading and the event-dispatch thread
I have a swing GUI named SpyBiteDemo, it will call another class(Parser) and do some calculations and show some data in a jtable inside this GUI(SpyBiteDemo). I have a jbutton1 and I want to when click on it to show my timer to begin like 1,2,3,4,5,....seconds
what happens is my timer is running correctly however it does not show value unless it is done with all the program that is filling the jtable which means the action it is detecting is I perform the action after jtable appears.
I am a complete newbie on Java for event listeners and I have searched timer, timer task, schedule, everything and could not understand what's wrong.I also tried while(true) and did not fix it.I also tried duration of 1000,0,everything no affects.
I tried to use action command,sleeping the thread, it did not help.here is what I did:
public class SpyBiteDemo extends javax.swing.JFrame {
/**
* Creates new form SpyBiteDemo
*/
private long startTime;
Timer timer = new Timer(0, new TimerListener());
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent aEvt) {
long time = (System.currentTimeMillis() - startTime) / 1000;
label3.setText(time + " seconds");
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
startTime = evt.getWhen();
String SeedUrl = jTextField1.getText();
Parser P = new Parser(this);
jTable2.setVisible(true);
}
}
it will start showing label3 value only after it is filling the jtable on my jframe.I want the timer to start from when I am clicking the button.
with trashgod's links I had come up with this example which is comepletely runnable on your machine, this works perfect except that when I the program finishes, it does not stop the timer since I don't know where to do it, I know I should do it in addPropertyChangeListener, however I do not have timer value.
/*
* 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.
*/
package javaapplication7;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
/**
* #see http://stackoverflow.com/a/25526869/230513
*/
public class DisplayLog {
private static final String NAME = "C:\\wamp\\bin\\mysql\\mysql5.6.17\\bin\\scosche.sql";
private static class LogWorker extends SwingWorker<TableModel, String> {
private final File file;
private final DefaultTableModel model;
private LogWorker(File file, DefaultTableModel model) {
this.file = file;
this.model = model;
model.setColumnIdentifiers(new Object[]{file.getAbsolutePath()});
}
#Override
protected TableModel doInBackground() throws Exception {
BufferedReader br = new BufferedReader(new FileReader(file));
String s;
while ((s = br.readLine()) != null) {
publish(s);
}
return model;
}
#Override
protected void process(List<String> chunks) {
for (String s : chunks) {
model.addRow(new Object[]{s});
}
}
#Override
protected void done() {}
}
private void display() {
JFrame f = new JFrame("DisplayLog");
JLabel m=new JLabel("time");
JButton jb=new JButton("run");
f.add(jb,BorderLayout.BEFORE_FIRST_LINE);
f.add(m, BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel();
JTable table = new JTable(model);
JProgressBar jpb = new JProgressBar();
f.add(jpb, BorderLayout.NORTH);
f.add(new JScrollPane(table));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
LogWorker lw = new LogWorker(new File(NAME), model);
lw.addPropertyChangeListener((PropertyChangeEvent ev) -> {
SwingWorker.StateValue s = (SwingWorker.StateValue) ev.getNewValue();
jpb.setIndeterminate(s.equals(SwingWorker.StateValue.STARTED));
});
lw.execute();
int timeDelay = 0;
ActionListener time;
time = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
m.setText(System.currentTimeMillis() / 1000 + "seconds");
}
};
Timer timer=new Timer(timeDelay, time);
timer.start();
if(lw.isDone())
timer.stop();
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new DisplayLog().display();
});
}
}
It looks like the rows that comprise your table come from the network with variable, indeterminate latency. As shown here, you can load data into your TableModel in the background of a SwingWorker. As shown here, you can calculate intermediate progress in a way that makes sense for your application and display it in a PropertyChangeListener.
I'm attempting to make a program in java that uses a robot to press a specific key every few seconds. It has a GUI with a start and stop button and a label which tells which state its in. I've got everything working so far except that when I click "start" it runs the loop for my robot function (which is infinite) it doesn't enable the stop button like I thought it would. I know its something stupid with where the infinite loop is placed but I'm not sure how to make it work correctly.
I don't do a lot of java work, this was just a fun thing I thought to try but got stuck part way through. Any help is appreciated.
import java.awt.AWTException;
import java.awt.FlowLayout;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Main extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
private static boolean running = false;;
private JButton start_button;
private JButton stop_button;
private JLabel tl;
private static int i = 0;
Robot robot;
void start() {
JFrame frame = new JFrame("Helper");
tl = new JLabel("Running: " + running);
start_button = new JButton("Start");
stop_button = new JButton("Stop");
stop_button.setEnabled(false);
frame.add(tl);
frame.add(start_button);
frame.add(stop_button);
frame.setSize(300, 100);
frame.setVisible(true);
frame.setLayout(new FlowLayout());
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setLocation(400, 400);
try {
robot = new Robot();
} catch (AWTException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
robot.setAutoDelay(200);
start_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(false);
stop_button.setEnabled(true);
running = true;
tl.setText("Running: " + running);
while (running) {
robot_loop(robot);
}
}
});
stop_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(true);
stop_button.setEnabled(false);
running = false;
tl.setText("Running: " + running);
}
});
}
public static void main(String[] args) {
new Main().start();
}
private static void robot_loop(Robot robot) {
robot.keyPress(KeyEvent.VK_NUMPAD0);
robot.keyRelease(KeyEvent.VK_NUMPAD0);
System.out.println("numpad 0 pressed! - " + i);
i++;
}
}
I've adapted my comment into an answer.
The actionPerformed method of those event listeners are invoked on Swing's event dispatch thread, and since you're entering into an infinite loop, it'll cause the GUI to freeze. You could create a thread inside of your actionPerformed method and do your work inside of the new thread. Though the next issue you'd run into is finding a nice way to stop the thread whenever the user presses the stop button.
What's cool is that you've already got all the logic to do this in your code. So getting it to work is as simple as changing:
start_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(false);
stop_button.setEnabled(true);
running = true;
tl.setText("Running: " + running);
while (running) {
robot_loop(robot);
}
}
});
To do your work on its own thread:
start_button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start_button.setEnabled(false);
stop_button.setEnabled(true);
running = true;
tl.setText("Running: " + running);
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override public void run() {
while (running) {
robot_loop(robot);
}
}
});
}
});
The code above makes use of the executors framework (java.util.concurrent.*) rather than directly creating a thread. Another alternative as nachokk suggested would be to use a timer java.util.Timer or javax.swing.Timer (either should be fine in this case).
You can do something like this using SwingTimer
int delay = 400*1000;// you can inject this property
ActionListener taskPerformer = new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt2) {
robot_loop(robot);
}
};
Timer timer = new Timer(delay, taskPerformer);
timer.start();
I have a GUI application that uses an InputVerifier to check the content of text fields before yielding the focus. This is all very normal. Yesterday, however, discovered a problem - it seems to be a bug, but I cannot find any mention of it anywhere. Before I report this as a bug, I thought I would ask: am I missing something obvious here?
Situation:
A set of text fields with InputVerifiers.
Listeners for FocusLost and FocusGained on all controls, so I can see what is happening.
A separate thread uses a DefaultKeyboardFocusManager to report (every 2 seconds) which control has the focus.
I place invalid data in a JTextField in the middle of the form, and try to leave the control.
If I try to move away from this control using the mouse, or using the tab-key, I cannot. The FocusLost event does not fire and the control properly retains the focus.
However, if I try to move away from the control in reverse tab order, using Shift-Tab, sometimes the FocusLost event fires. If this happens, the separate thread reports that no control has the focus, i.e., getFocusOwner() returns null.
Edit: below is a small sample program that shows the problem. The problem has nothing to do with the extra thread - the thread is just there to make the problem more obvious. If there is a race-condition, it is somewhere in Swing.
To see the problem, go to the second text box and empty it. The control should retain the focus, and does so unless you leave it by pressing shift-tab. Unlike the full application, the error seems to occur here 100% of the time. This is true both under OpenJDK 6 and under Oracle Java 7.
This is almost too obvious to be a bug, plus it happens in multiple Java environments. Hence, my suspicion that I am missing something obvious. Anyone?
public class FocusBugDemo extends JFrame {
static JTextField txtOne = new JTextField("Ignore this control");
static JTextField txtTwo = new JTextField("Delete this text, then press shift-tab");
static JLabel lblFocus = new JLabel("");
static KeyboardFocusManager kfm = new DefaultKeyboardFocusManager();
public static void main(String[] args) {
new FocusBugDemo();
Thread t = new Thread() {
#Override
public void run() {
while(true) {
Component c = kfm.getFocusOwner();
String focusInfo = "elsewhere";
if (c == null) { focusInfo = "null";
} else if (c == txtOne) { focusInfo = "txtOne";
} else if (c == txtTwo) { focusInfo = "txtTwo";
}
lblFocus.setText(System.currentTimeMillis() + " - Focus owner " + focusInfo);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
};
t.start();
}
private FocusBugDemo() {
super("Focus bug demo");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setPreferredSize(new Dimension(300,100));
setLayout(new GridLayout(3,1));
NotEmpty validator = new NotEmpty();
txtOne.setInputVerifier(validator);
txtTwo.setInputVerifier(validator);
add(txtOne);
add(txtTwo);
add(lblFocus);
pack();
setVisible(true);
}
private class NotEmpty extends InputVerifier {
#Override
public boolean verify(JComponent input) {
JTextField txtField = (JTextField) input;
return (txtField.getText().length() > 0);
}
}
}
Now reported to Oracle as bug 7167871.
Using your sscce, I am unable to reproduce the effect you describe on Mac OS X, Java 6, which supports #CatalinaIsland's observation. In particular, focus never leaves an empty text field using either tab or shift-tab; focus becomes null only when the frame is deactivated.
I see two threads accessing multiple fields with no synchronization at all. At a minimum, you should use EventQueue.invokeLater() in t to update the GUI, as described in Concurrency in Swing and show below.
The broader question is this: What focus problem are you trying to solve using t?
import java.awt.Component;
import java.awt.DefaultKeyboardFocusManager;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.KeyboardFocusManager;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class FocusDemo {
private static final JTextField txtOne =
new JTextField("Ignore this control");
private static final JTextField txtTwo =
new JTextField("Delete this text, then press shift-tab");
private static final JLabel lblFocus = new JLabel("");
public static void main(String[] args) {
new FocusDemo();
Thread t = new Thread() {
#Override
public void run() {
while (true) {
EventQueue.invokeLater(new Runnable() {
KeyboardFocusManager kfm =
new DefaultKeyboardFocusManager();
#Override
public void run() {
Component c = kfm.getFocusOwner();
String focusInfo = "elsewhere";
if (c == null) {
focusInfo = "null";
} else if (c == txtOne) {
focusInfo = "txtOne";
} else if (c == txtTwo) {
focusInfo = "txtTwo";
}
lblFocus.setText(System.currentTimeMillis()
+ " - Focus owner " + focusInfo);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
}
}
};
t.start();
}
private FocusDemo() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Focus bug demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setPreferredSize(new Dimension(300, 100));
f.setLayout(new GridLayout(3, 1));
NotEmpty validator = new NotEmpty();
txtOne.setInputVerifier(validator);
txtTwo.setInputVerifier(validator);
f.add(txtOne);
f.add(txtTwo);
f.add(lblFocus);
f.pack();
f.setVisible(true);
}
});
}
private class NotEmpty extends InputVerifier {
#Override
public boolean verify(JComponent input) {
JTextField txtField = (JTextField) input;
return (txtField.getText().length() > 0);
}
}
}