JTextFiled not updating when edited in a while loop - java

I want the JTextFiled to go up quickly as if it were a clock
the following code is inside a mouse listener.
Also t1 IS public and was declared before the main method in the class.
If you any questions pleas tell me!
ddd = 1. The rest of the switch statement is the same just instead of t1 its t2 or t3 or t4...
try {
String rol = null;
String har = null;
int rolx = 0;
int harx = 0;
int newl = 0;
switch (ddd) {
case 1:
rol = t1.getText();
har = numb.getText();
rolx = Integer.parseInt(rol);
harx = Integer.parseInt(har);
newl = rolx + harx;
while (harx > 0) {
harx --;
rolx ++;
Thread.sleep(10);
System.out.println(""+rolx);
t1.setText(""+rolx);
}
t1.setText(""+newl);
break;

Anything you do directly inside a Listener's event handling code holds the event handling thread, preventing any GUI activity.
You need to either move the loop to another thread, or use some form of timer instead.

Related

Moving and organizing Java codes on another files

I would like to create a new file and put this block of action buttons codes to be organized on the other files i dont know how i would be able to move it.
Iam making this code and its a little bit disorganized and im having a hard time on what is their purpose.
Is there any other easier method to create a actionlistener codes?
public void buttonAction (){
bgButton[0].addActionListener(e -> {
bgPanel[0].setVisible(false);
bgPanel[0].remove(bgButton[0]);
bgPanel[1].setVisible(true);
});
for (int a = 3,c=0 ; a <12; a++, c++){
final int b=c;
final int d=a;
bgButton[a].addActionListener(e -> {
if (input>=0&&input <=9&&Num[b]!=0){
input = Num[b]*10;
if(input!=0)
createObject(1,12,283,245,85,61,numFile[b]);//12
bgButton[d].setEnabled(false);
Number[b]=-1;
} else if (input >9&&input<100&&input%10==0&&Num[b]!=0&&buttonClicked){
input += Num[b];
buttonClicked = !buttonClicked;
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
Number[b]=-1;
}else if (Num[b]==0&&buttonClicked){
input += Num[b];
createObject(1,13,432,245,85,61,numFile[b]);//13
bgButton[d].setEnabled(false);
buttonClicked = !buttonClicked;
Number[b]=-1;
}
System.err.println("total " + input);
bgPanel[1].revalidate();
bgPanel[1].repaint();
});
}
bgButton[14].addActionListener(e -> {
for (int c =0 ,a=3; c <9; a++,c++){
final int b = a;
final int d=c;
int firstNum = input/10;
int secondNum=input%10;
if (Number[c]==-1&&bgButton[13]!=null){
attack = input;
generateSoloNum(d);
bgButton[b].setEnabled(true);
updateButtonIcon(b,64,48,numFile[d]);
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
Number [c]=0;
input =0;
}
}
buttonClicked = true;
bgButton[13]=null;
});
createObject(1,15,149,244,50,38,"res/imageAssets/x.png");//15
bgButton[15].addActionListener(e -> {
input = 0;
bgPanel[1].remove(bgButton[12]);
bgPanel[1].remove(bgButton[13]);
bgPanel[1].revalidate();
bgPanel[1].repaint();
buttonClicked = true;
for (int a = 3,c=0; a<12;a++,c++){
bgButton[a].setEnabled(true);
Number [c]=0;
}
});
}
I tried import Main.UI; and import Main.Action;
Action is the new file that i want to move it into.
tried using chat gpt but it makes no sense
All methods in Java need to be associated with a class. You cannot move methods from listeners into separate files.
You can create separate listener implementations that include these action methods. If you refactor that way you'll give instances of those listener implementations to your Swing frame and call them instead of keeping them in one big Swing class.
I think this is a good idea. You would change implementations by injecting new classes instead of modifying the frame code.
Each of those addActionListener lambdas would become part of separate classes.
Swing UI code tends to turn into walls of code if you're not careful. I have never seen a well decomposed Swing UI application posted here.

Do I use a SwingWorker?

While my code sends http calls I want it to alter the JLabel on a window (basically showing approximately how much time is left before the http calls end). I have been looking into SwingWorkers due to another question I asked here, but I'm not sure how I use it. The code I am writing basically has a loop to send the calls, each time timing how long it takes to run the call, calculates the approximate time left and then sends this to the JLabel (NB the JLabel is in a different instantiated object).
Most SwingWorker examples show a function continuing in the background that is not affected by the worker thread (e.g. a counter based entirely on time rather than being altered by the code). If this is the case then isn't the alteration of the JLabel just part of the worker thread as it the code runs through new loop -> calculate time & make call -> alter JLabel? I'm probably wrong but then how do I have the JLabel altered by the code rather than a independent thread?
One of my issues was that when I initially set my code up there was nothing changing in the JLabel.
Here is my code:
package transcription.windows;
import javax.swing.*;
import java.awt.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PleaseWaitWindow {
private JLabel pleaseWaitJLabel = new JLabel("Please wait");
private GridBagConstraints containerGbc = new GridBagConstraints();
private Container contentPaneContainer = new Container();
private JFrame pleaseWaitJFrame;
public JLabel getPleaseWaitJLabel() {
return pleaseWaitJLabel;
}
public JFrame setPleaseWaitWindow() {
pleaseWaitJFrame = new JFrame();
contentPaneContainer = setContentPane();
pleaseWaitJFrame.setContentPane(contentPaneContainer);
pleaseWaitJFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
pleaseWaitJFrame.setTitle("");
pleaseWaitJFrame.setSize(350, 150);
pleaseWaitJFrame.setLocationRelativeTo(null);
pleaseWaitJFrame.setVisible(true);
return pleaseWaitJFrame;
}
private Container setContentPane() {
containerGbc.insets.bottom = 1;
containerGbc.insets.top = 2;
containerGbc.insets.right = 1;
containerGbc.insets.left = 1;
containerGbc.weightx = 1;
containerGbc.weighty = 1;
contentPaneContainer.setLayout(new GridBagLayout());
contentPaneContainer.setSize(800, 700);
setPleaseWaitJLabel();
return contentPaneContainer;
}
private void setPleaseWaitJLabel() {
containerGbc.gridx = 2;
containerGbc.gridy = 5;
containerGbc.gridwidth = 2;
containerGbc.gridheight = 1;
contentPaneContainer.add(pleaseWaitJLabel, containerGbc);
}
public void setJLabelDisplay(String displayTime) {
pleaseWaitJLabel.setText(displayTime);
}
public void closeWindow() {
pleaseWaitJFrame.dispose();
}
}
Method that is part of the ServiceUpload class:
public String cuttingLoop(String mpBase64Piece, String jobName, String email) {
Integer numberOfPiecesMinusEnd = (int) Math.ceil(mpBase64Piece.length() / 500000.0);
List<String> base64List = new ArrayList<>();
for (int i = 0; i < numberOfPiecesMinusEnd; i++) {
if (mpBase64Piece.length() >= 500000) {
base64List.add(mpBase64Piece.substring(0, 500000));
mpBase64Piece = mpBase64Piece.substring(500000);
}
}
base64List.add(mpBase64Piece);
pleaseWaitWindow = new PleaseWaitWindow();
pleaseWaitWindow.setPleaseWaitWindow();
for (int n = 0; n < base64List.size(); n++) {
numberOfLoopsLeft = numberOfPiecesMinusEnd - n;
Stopwatch stopwatch = null;
String tag;
Stopwatch.createStarted();
if (base64List.get(n) != null) {
if (n == 0) {
tag = "start";
} else if (n == base64List.size() - 1) {
tag = "end";
} else {
tag = "middle";
}
stopwatch = Stopwatch.createStarted();
response = providerUpload.executeUploadHttp(base64List.get(n), jobName, tag, email);
stopwatch.stop();
}
long oneLoopTime = stopwatch.elapsed(TimeUnit.SECONDS);
pleaseWaitWindow.setJLabelDisplay(numberOfLoopsLeft*oneLoopTime+" seconds remaining");
LOGGER.info("complete");
}
pleaseWaitWindow.closeWindow();
return response;
}
One of my issues was the code did not show the 'JLabel' when a 'SwingWorker' isn't used with the above code.
It's best you split up your code into areas of responsibilities. Let's go with three: 1. the worker (ie the upload); 2. the display (ie the JLabel update); 3. integration of the two (the first two are independent of each other, so you'll need something to tie them together).
Abstracting from the actual work, you can use standard interfaces. The first one is just a Runnable, ie not taking any parameters and not returning anything. The second one is a Consumer<String> because it takes a String (to display) but doesn't return anything. The third will be your main control.
Let's start with the control because that's simple:
Consumer<String> display = createDisplay();
Runnable worker = createWorker();
CompletableFuture.runAsync(worker);
This will start the worker in a separate Thread which is what it sounds like you want.
So here's your uploader:
Consumer<String> display = // tbd, see below
Runnable worker = () -> {
String[] progress = {"start", "middle", "finish"};
for (String pr : progress) {
display.accept(pr);
Thread.sleep(1000); // insert your code here
}
}
Note that this worker actually does depend on the consumer; that is somewhat "unclean", but will do.
Now for the display. Having defined it as a Consumer<String>, it's abstract enough that we can just print the progress on the console.
Consumer<String> display = s -> System.out.printf("upload status: %s%n", s);
You however want to update a JLabel; so the consumer would look like
Consumer<String> display = s -> label.setText(s);
// for your code
s -> pleaseWaitWindow.getPleaseWaitLabel().setText(s);
Your actual question
So if you do that, you will notice that your label text doesn't get updated as you expect. That is because the label.setText(s) gets executed in the thread in which the worker is running; it needs to be inserted in the Swing thread. That's where the SwingWorker comes in.
The SwingWorker has a progress field which is what you can use for your labels; it also has a doInBackground() which is your actual upload worker thread. So you end up with
class UploadSwingWorker {
public void doInBackground() {
for(int i = 0; i < 3; i++) {
setProgress(i);
Thread.sleep(1000); // again, your upload code
}
}
}
So how does that update your label? The setProgress raises a PropertyChangeEvent you can intercept; this done using a PropertyChangeListener with the signature
void propertyChange(PropertyChangeEvent e)
This is a functional interface, so you can implement this with a lambda, in your case
String[] displays = {"start", "middle", "finish"};
updateLabelListener = e -> {
int index = ((Integer) e.getNewValue()).intValue(); // the progress you set
String value = displays[index];
label.setText(value);
}
and you can add it to the SwingWorker using
SwingWorker uploadWorker = new UploadSwingWorker();
uploadWorker.addPropertyChangeListener(updateLabelListener);
uploadWorker.execute(); // actually start the worker
Simpler
Note that I myself have never used a SwingWorker this way. The much simpler way to get around the problem that the GUI is not updated from within your worker thread is to call the GUI update using SwingUtilities.invokeLater().
Coming back to the initial Consumer<String> I brought up, you can do
Consumer<String> display = s -> SwingUtilities.invokeLater(
() -> pleaseWaitWindow.getPleaseWaitLabel().setText(s)
);
and that should do. This allows you to keep your worker in the more abstract Runnable and use the usual scheduling mechanisms to run it (ExecutorService.submit() or CompletableFuture.runAsync() for example), while still allowing to update the GUI on a similarly simple level.

Why Is While Loop Not Working?

I have the following nested class inside a class that implements MouseListener:
public void plusOne()
{
int reference = 0;
int status = 0;
System.out.println("BEGIN");
System.out.println(dateArray[reference].selected);
while (dateArray[reference].selected = false)
{
reference++;
System.out.println("SUCCESS");
}
while (dateArray[reference].selected = true)
{
reference++;
}
while (true)
{
if (dateArray[reference].status == 2)
{
dateArray[reference].status =1;
status =2;
break;
}
if (dateArray[reference].status == 3)
{
dateArray[reference].status =0;
status = 3;
break;
}
dateArray[reference].selected = true;
reference++;
dateArray[reference].status = status;
}
}
System.out.prinln("BEGIN") is executed.
System.out.prinln(dateArray[reference].selected) is also executed. It should display the value "false" but it ALSO displays an error message of type
"Exception in thread "AWT-EventQueue-0" false
The next while loop is not executed.
Help Please!
you wrote wrong syntax in java, trace your code and try like example code below
while (dateArray[reference].selected == false){
while (dateArray[reference].selected == true){
Your main problem is that you're trying to force linear command line program type code into a single threaded event-driven programming environment, and this will likely completely freeze your GUI. For example this:
public void plusOne() {
int reference = 0;
int status = 0;
System.out.println("BEGIN");
System.out.println(dateArray[reference].selected);
while (!dateArray[reference].selected) {
reference++;
System.out.println("SUCCESS");
}
// ... here
Is fine in a simple console program since program code flow is dictated solely by you the programmer, but note that you'd likely have code within the while loop for user to enter code, and the program would wait patiently for the user to do this, then the loop would check the condition and decide whether to re-ask the user for input or proceed beyond the loop.
But this won't work in a single threaded Swing program since that while loop will block the Swing event dispatch thread and would then completely freeze the GUI. The solution is to get rid of the while loop and instead use if blocks and instance fields and vary your method's response depending on the state of the fields. The details of how you would implement this would depend on the details of your program and code, something we don't really know yet, but that you should tell and show us.
The = should be ==
public void plusOne()
{
int reference = 0;
int status = 0;
System.out.println("BEGIN");
System.out.println(dateArray[reference].selected);
while (!dateArray[reference].selected) //-----------
{
reference++;
System.out.println("SUCCESS");
}
while (dateArray[reference].selected) //-------------
{
reference++;
}
while (true)
{
if (dateArray[reference].status == 2)
{
dateArray[reference].status =1;
status =2;
break;
}
if (dateArray[reference].status == 3)
{
dateArray[reference].status =0;
status = 3;
break;
}
dateArray[reference].selected = true;
reference++;
dateArray[reference].status = status;
}
}

Problems of checkName() and storing user input to ArrayList and boolean[]

Here I have a GUI window and it basically ask the user to select a JRadioButton and type something in a JTextField, then choose confirm/cancel.
It is a project which we have to make a UML-to-Java text file. User would enter class information and choose a UML relationship, and this programme have to print out the Java clsas text on a JTextField. Just like when you create a new class in eclipse.
what I want to do is make a boolean[] to store an array of booleans, when user selects JRadioButton_A it'll store true and when user select JRadioButton_B it'll store false.And also I want the things typed in JTextField to be checked by a checkName(), if the method returns false, the string will be stored in an ArrayList.
Below is my code - there's some problems in getName() method and the boolean[] for storing true and false. When user needs to input name again, it would save the discarded sting/boolean into the array. (Sorry for my bad english!) Is there any better way to make this programme? I feel like I am complicating things and there should be a simpler way to make it.
Here's the UI stuffs asking user to enter class information. User have to select public/private and then type in class name and JTextField
private class Handler implements ActionListener{
public void actionPerformed(ActionEvent event){
String name = inputClassName.getText();
classObject.addName(name);
while (classObject.checkName(name) == true){
JOptionPane.showMessageDialog(null, "Class name invalid. " +
"\nEntered name should not contain java keywords or equal to other existing names. " +
"\nPlease try again."); // doesn't work
name = inputClassName.getText();
classObject.addName(name);
}// end if
JOptionPane.showMessageDialog(null, "Class saved."); // doesn't work
name = inputClassName.getText();
classObject.addName(name);
}// end actionPerformed()
}// end Handler class
private class Handler2 implements ActionListener{
public void actionPerformed(ActionEvent event){
boolean b = true;
b = classObject.setPP();
}
}
private class Handler3 implements ActionListener{
public void actionPerformed(ActionEvent event){
boolean b = false;
b = classObject.setPP();
}
}
Here's the methods for storing the inputs to the ArrayList and boolean[]
Scanner input = new Scanner(System.in);
JavaKeywords keyObject = new JavaKeywords();
private ArrayList<String> className = new ArrayList<String>();
private String name = new String();
private int size = className.size();
private Boolean[] bArray = new Boolean[size];
public boolean checkName(String name){
boolean check = true;
for (int i=0; i<=size; i++){
if (keyObject.containsKeyword(className.get(i)) || name.equals(className.get(i))){
boolean o = false;
check = o;
}// end if
}// end for
return check;
}// end checkName
public boolean setPP(){
boolean b = true;
return b;
}
public void addPP(Boolean[] bArray){
this.bArray = bArray;
for (int i=0; i>=size; i++){
bArray[i] = setPP();
}
}// add a Array of boolean. for className[i], its class type = item[i] in bArray.
// public = true, private = false
public String getPublicPrivate(){
String p = "";
for (int i =0; i<=size; i++){
if(bArray[i]=true)
p = "public";
else
p = "private";
}
return p;
}
Solved
Solution: store the string className and boolean isPrivate in a class and make the class into an ArrayList can save me from all the trouble. But then i faced anther problem, that is the checkName() doesn't work after I changed my code.
here is the ActionListener
private class Handler implements ActionListener{
public void actionPerformed(ActionEvent event){
VirtualClass virtualObject = new VirtualClass();
classObject.addClass(virtualObject);
String name = inputClassName.getText();
virtualObject.className = name;
if (classObject.checkName(name) == false){
JOptionPane.showMessageDialog(null, "Class name invalid. " +
"\nEntered name should not contain java keywords or equal to other existing names. " +
"\nPlease try again."); // Always return "invalid" message
} else {
JOptionPane.showMessageDialog(null, "Class saved.");
name = inputClassName.getText();
virtualObject.className = name;
}
if (event.getSource() == publicButton) {
virtualObject.isPrivate = false;
} else if (event.getSource() == privateButton) {
virtualObject.isPrivate = true;
}
}// end actionPerformed()
and here is the checkName() method
public boolean checkName(String name){
boolean check = true;
for (int i=0; i<=size; i++){
if (keyObject.containsKeyword(classes.get(i).className) || name.equals(classes.get(i).className)){
boolean o = false;
check = o;
}// end if
}// end for
return check;
}// end checkName
For containsKeyword() in checkName() I've used a JavaKeywords class from How to check if the class name is valid? by #MrLore.
Probably what I would do is create a simple class to represent your fields so you don't have to use multiple lists at all.
public class VirtualClass {
public boolean isPrivate;
public String className = "Object";
}
ArrayList<VirtualClass> classes = new ArrayList<VirtualClass>(0);
public void addClass(VirtualClass clazz) {
classes.add(clazz);
}
Otherwise you will have to create a second list of some kind to hold the public/private. You will just have to change them in parallel.
// in actionPerformed
ClassObject.VirtualClass clazz = new ClassObject.VirtualClass();
clazz.isPrivate = rbPrivate.isSelected();
clazz.className = tfClassName.getText();
classObject.addClass(clazz);
And just ignore the listening on the radio buttons since you technically do not need their states until you go to add the class to the list.
To access the fields later you just need to
for (VirtualClass clazz : classes) {
System.out.println((clazz.isPrivate ? "private" : "public") + " " + clazz.className);
}
// or something like
for (int i = 0; i < classes.size(); i++) {
System.out.println(classes.get(i).className + ":");
if (classes.get(i).isPrivate) {
System.out.println(" private");
} else {
System.out.println(" public");
}
}
I'm not entirely convinced by your over all approach. What I think "should"/"could" happen is, the user enters all the information you ask, they hit "accept", you valid the information that the user has entered and if it is correct, you create a new object representing the results of this input as you need.
I would, personally, avoid using an array of booleans, or at least, expose them differently. The main problem I have with it is keeping it all straight in my head, what does the element at 0 actually mean?
Instead, I would provide getter/setters on the ClassName class that allowed me to set/get particular properties. You could, of course, keep the values in an array internally, but anyone using the class wouldn't need to know how you store these values.
The problem with your check name Handler is the fact you are blocking the Event Dispatching Thread with your while-loop
This will stop you program from responding to user input (and painting itself)
while (classObject.checkName(name) == true){
JOptionPane.showMessageDialog(null, "Class name invalid. " +
"\nEntered name should not contain java keywords or equal to other existing names. " +
"\nPlease try again."); // doesn't work
name = inputClassName.getText();
classObject.addName(name);
}// end if
Swing is a single threaded framework, meaning that all interactions and changes to the UI are expected to be executed within the context of the EDT. Equally, anything that blocks or stops this thread from processing the Event Queue, will stop it from responding to new events, including repaint requests, making your program hang
Instead, you should simply check the name within a if-else statement. If it's valid, create the new ClassName object, if it's not, display a message and let the method exit.
Check out Concurrency in Swing for more details

Java enum and loop question

I have a list of Nodes which I loop through:
for (int i = 0; i < aNodes.getLength(); i++) {
//
}
For example, assume the list contains 12 items, and that I know 3 items represent a row in a table, and the next 3 belong to the following row. That means my list of 12 Nodes originates from an HTML table containing 4 rows.
After each row I want to do something, for example, create a new object and populate it...or whatever. I have a solution:
ArrayList<Account> mAccounts = new ArrayList<Account>();
Account account = new Account();
for (int i = 0; i < aNodes.getLength(); i++) {
String nodeValue = aNodes.item(i).getNodeValue();
boolean isNewRow = (i % COLS_PER_ROW == 0);
if (isNewRow) {
account = new Account();
mAccounts.add(account);
}
switch (i % COLS_PER_ROW) {
case ACCOUNT_POS:
account.setAccount(nodeValue);
break;
case BALANCE_POS:
account.setBalance(nodeValue);
break;
case DATE_POS:
account.setDate(nodeValue);
break;
}
}
But there are numerous things I don't like about this solution:
An Account instance is created twice first time, once outside the loop and then once because a new row is detected.
It uses integer constants ACCOUNT_POS=0, BALANCE_POS=1, DATE_POS=2...this doesn't feel very nice and I think I should be using an enum.
I can't use an enum with the loop variable 'i'.
I can't use a for each loop since Nodes doesn't implement the correct interface,
Can anyone suggest a better way of doing this that solves the list of things I don't like about it?
Thank you.
You can increment i by COLS_PER_ROW instead of 1 and then write:
for (int i = 0; i < aNodes.getLength(); i += COLS_PER_ROW) {
account = new Account();
String account = aNodes.item(i).getNodeValue();
account.setAccount(account);
String balance = aNodes.item(i+1).getNodeValue();
account.setBalance(balance);
String date = aNodes.item(i+2).getNodeValue();
account.setDate(date);
mAccounts.add(account);
}
I can't use an enum with the loop variable 'i'.
No, but you can use:
for(SomeEnum item : SomeEnum.values()){
// code here
}
You're working with a position based file, so, if I understand you correctly, you've a structure which is organized as follows:
account;
balance,
date;
Knowing this, maybe you could better deal with the index in a while loop, rather than using a for loop, as follows:
int i = 0;
while (i < aNodes.getLength()) {
account = new Account();
account.setAccount(aNodes.item(i).getNodeValue());
account.setBalance(aNodes.item(i+1).getNodeValue());
account.setBalance(aNodes.item(i+2).getNodeValue());
mAccounts.add(account)
i += 3;
}
I would also suggest to extract the account-filling-code in a new method, like 'extractAccountFromNodes', and then call it inside the loop; so the loop could become:
int i = 0;
while (i < aNodes.getLength()) {
mAccount.add(extractAccountFromNodes(aNodes, i));
i += 3;
}
with the method extractAccountFromNodes like this:
private Account extractAccountFromNodes(Nodes nodes, int position) {
account = new Account();
account.setAccount(nodes.item(i).getNodeValue());
account.setBalance(nodes.item(i+1).getNodeValue());
account.setBalance(nodes.item(i+2).getNodeValue());
return account;
}

Categories