Getting a position in a JTextArea and using a timer - java

I'm working on a school project and I'm hoping if you guys could help me.
I have this:
JTextArea processTxt = new JTextArea();
And when I press this button,
JButton sortBtn = new JButton("SORT!");
This would display this on processTxt.
public void sort(String type, String order, int size) {
int tempInt, int1, int2;
processTxt.append("Pass 0:\n");
for (int i=0; i<size; i++) {
if (type.equals("int")) {
processTxt.append(intList.get(i)+" ");
}
}
processTxt.append("\n");
for (int i=0; i<size-1; i++) {
int x=0;
processTxt.append("Pass "+(i+1)+": \n");
while (x<size-1) {
int y = x + 1;
if (type.equals("int")) {
int1 = intList.get(x);
int2 = intList.get(y);
if (order.equals("asc")) {
if (int1 >= int2) {
tempInt = int1;
intList.set(x, int2);
intList.set(y, int1);
}
}
else if (order.equals("desc")) {
if (int1 <= int2) {
tempInt = int1;
intList.set(x, int2);
intList.set(y, int1);
}
}
}
for (int z=0; z<size; z++) {
if (type.equals("int")) {
processTxt.append(intList.get(z)+" ");
}
}
processTxt.append("\n");
x++;
}
}
processTxt.append("Sorted:\n");
for (int i=0; i<size; i++) {
if (type.equals("int")) {
processTxt.append(intList.get(i)+" ");
}
}
}
I was thinking:
How could I get the position of int1 on the text area?
Is there any way that I could use a timer to delay the output on the text area? I have used thread.sleep() but it won't work.

1) how could i get int1's position on the textarea;
Apart from physically searching the text, you could maintain reference to the location that it would have been inserted, before you inserted.
You could use the JTextArea's caret position if you are inserting text at the current cursor position, otherwise, you will need to determine the index point from within the Document you are updating.
2) is there any way that i could use a timer to delay the output on the textarea? i have used thread.sleep() but it wont work.
Yes, but understand, this becomes increasingly more complex.
You could use a Swing Timer which will would trigger a callback at a regular interval, allowing you to update state of the sort and update the UI. This is useful as it triggers the callback within the context of the Event Dispatching Thread, making it safe to update the UI from within.
It does, how ever, increase the complexity, as the Timer acts as a kind of pseudo loop.
You could use a SwingWorker instead. This would allow you to take your current code and run it within the doInBackground method, which is executed outside of the Event Dispatching Thread, allowing you to use Thread.sleep to inject a delay where you want them without affecting the UI.
The problem is, you will need to use the SwingWokers publish/process methods to push updates to the UI, this disconnects the time you make a change to the model to the time it appears in the UI, which may present issues with determining the location that a given update should occur (within the context of your first question).
Take a look at:
Concurrency in Swing
How to use Swing Timers
Worker Threads and SwingWorker
for more details

Related

How to NOT use thread.sleep for an image slide show in java

I'm trying to create something like an info screen that just scrolls through images (and maybe one day pdfs) that are uploaded with a small managment app I wrote.
After some reading I think I understand why I should NOT have Thread.sleep in my for loop. I read some posts on stackoverflow and other pages that teached me not to do it.
Apparently I'm supposed to use ExecutorService for something like this, but I can't wrap my head around it.
So after preparing my GUI and everything else I finally call my showImages and stay in this while loop.
After each loop I reload the records from the file systems, so any changes are represented on the screen.
I created a metafile containing the preferred delay for each image and just sleep for the amount of time.
private void showImages() {
//noinspection InfiniteLoopStatement
while (true) {
loadRecords();
for (ImageRecord record : records) {
System.out.println("Showing: " + record.getFilename());
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
int delay = record.getDurationAsInt();
System.out.println("Waiting for " + delay + " milliseconds");
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
So if I understand correctly I could use something like
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(showImage(),0, delay, TimeUnit.MILLISECONDS);
But this would trigger the showImage() every "delay" millisecond and I still need to increment some counter, to get to the next image in my records.
Could someone push me in the right direction? I'm kinda lost at the moment.
All the comments on my question can be considered good ideas an and one of them also lead me to my final result. But it in the end, the matter was, that I did not understand how to implement a timer into my loop.
Or in other words, the loops had to be gone and instead use a counter and set the delay for the follow up record inside the timer loop.
private void loopImages() {
loadRecords();
recordSize = records.size();
int delay = records.get(1).getDurationAsInt();
timer = new Timer(delay, e -> showImages());
timer.start();
}
private void showImages() {
if (recordIndex == recordSize) {
loadRecords();
recordSize = records.size();
recordIndex = 0;
}
ImageRecord record = records.get(recordIndex);
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
recordIndex++;
if (recordIndex < recordSize) {
record = records.get(recordIndex);
int delay = record.getDurationAsInt();
timer.setDelay(delay);
}
}

How can i go to next iteration when i click a buttom in a while loop?

I am making a quiz. I store my question in an array. So I use a while loop to display the question one by one. When I click the button, the counter +1 and go to next iteration which is next question. However it seems like that the it does not work out.
final int[] k = {0};
while (k[0] <2){
ql.setText(qa[k[0]][0]);
ql.setVisible(true);
for (int l = 0; l < 4; l++) {
ch[l].setVisible(true);
ch[l].setText(String.valueOf((int) (50 * Math.random())));
}
rc = (int) (4 * Math.random());
ch[rc].setText(qa[k[0]][1]);
nx.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
k[0]++;
}
});
}
What is the problem here?
Thanks!!
You have to invert your loop to work in an event-driven style. So what I'm thinking is, you should have the button click drive the "loop" in that it will only activate the questions provided your counter has not run down, otherwise it will just ignore any more presses.
Something like:
private void showQuestion(int question) {
ql.setText(qa[question][0]);
ql.setVisible(true);
for (int l = 0; l < 4; l++) {
ch[l].setVisible(true);
ch[l].setText(String.valueOf((int) (50 * Math.random())));
}
int rc = (int) (4 * Math.random());
ch[rc].setText(qa[question][1]);
}
...
// In some method...
nx.addActionListener(new ActionListener() {
int nextQuestion = 1;
#Override
public void actionPerformed(ActionEvent e) {
if (k < 1) {
showQuestion(nextQuestion++);
}
}
});
showQuestion(0); // Shows the first question
There are a number of issues with your code but the most fundamental (focusing on your question) is that you are misunderstanding the difference between a loop in your code (e.g. for) and the GUI event loop. Loops in your code execute and then finish. GUI event loops wait until the user performs some action and then respond to it. It is not desirable to have a code loop waiting for some user action before continuing: that's just not how GUIs work.
I suggest you read the section in the swing tutorial on writing event listeners: here. It explains how to get your code correctly responding to events triggered by users.

Java - Sleeping thread to simulate changing image

So I've done a lot of searching and I can't quite find a solution to the problem I'm having. Designing GUIs with Java's JFrame seems fairly straight-forward, but updating them visually when it's more than just a setText call seems much more tricky, especially when you want to simulate some wait time for visual recognition in between. I'm not familiar with multi-threading and all of the solutions I found, I couldn't seem to get to work how I needed them to.
What I'm trying to do is to simulate dice rolling before landing on a particular side for a simple dice game. To do this, I'm just switching between different sides of dice images a set number of times (10, for example) every fraction of a second (150 ms in this case).
The interesting thing is that this code at its current state works EXACTLY how I want it to on start-up (which does a full 10 roll cycle once before accepting any user-input, but every component of the GUI is visible and working as expected). The problem is that when re-rolling, the GUI goes blank until all the thread sleeping is completed.
With my lack of knowledge about how this sort of thing, I don't understand why it would work the way I want the first time, but not any time after. Hopefully I can get some assistance about the right way to accomplish this or a fix for what I'm currently doing. Thanks!
private void rollDice()
{
for(int i = 0; i < 10; i++)
{
dice1 = (int) (Math.random() * 6) + 1;
dice2 = (int) (Math.random() * 6) + 1;
img1 = new ImageIcon(dice1 + ".png");
img2 = new ImageIcon(dice2 + ".png");
try
{
Thread.sleep(150);
}
catch(Exception e)
{
}
lbl1.setIcon(img1);
lbl2.setIcon(img2);
}
}
Try this I found here.
new Thread(new Runnable
{
public void run()
{
for(int count = 0; count < PREROLLS; count++)
{
//Add your for loop logic here!
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
//then update you icon here. I would start with one die if I were you.
imgDice1 = new ImageIcon(path + dice1 + ".png");
}
});
try { Thread.sleep(100); } catch(Exception e) {}
}
}
}).start();

How can I enable my code to have an actionListener for clicking of a button to retrieve data inputed to a GUI on multiple parts of the code?

I want to perform a computation in my code from netbeans, and the computed data is retrieved from a GUI which has an onButtonClick() action event and listener. I want to be able to access this data repeatedly at constant intervals to aid in the computation. My problem is that my code only allows the computation to be performed once and after that my solution returns a false answer as the variables inputted to the GUI become zeros.
How do I code such that I can constantly access these GUI inputted numbers at fixed time intervals without having to input them over and over?
It sounds like your GUI is too closely tied to your business logic.
You have many options, but one approach is to store the settings required for the calculation as fields in some class (perhaps split your calculation off into its own class). Then, when the GUI button is pressed, the configuration is taken from the GUI, parsed, and copied to the calculation object before the calculation is performed.
This automatically gives you a few advantages:
Since the configuration is now stored privately instead of in the GUI, GUI changes no longer affect the configuration of a running calculation -- unless you specifically want them to. You have full control.
Input validation (e.g. checking if numbers are in range, etc.) can be handled by the calculation object. This has a lot of good consequences, most notably your calculation can now rely on stated invariants and always assume that the configured settings are in valid ranges. Also you can initiate calculations through means other than your GUI and still have validation.
By separating your calculation code and configuration from the main application, you can now do all sorts of other things if you want, e.g. have multiple calculations running simultaneously on multiple threads, or whatever.
You may not want to do all of those things, but the point is you automatically get the ability to do them just by decoupling your business logic from your GUI.
Example:
class Calculation {
final int input1;
final int input2;
Calculation (int input1, int input2) {
if (input1 < 0 || input2 < 0)
throw new IllegalArgumentException("Inputs can't be negative."); // for example
this.input1 = input1;
this.input2 = input2;
}
void begin () {
...
}
}
Then when your GUI button is pressed:
int input1 = ...; // get value from gui
int input2 = ...; // get value from gui
calculation = new Calculation(input1, input2);
calculation.begin();
// now the actual settings are preserved in 'calculation' regardless of gui changes.
Of course there are many ways to organize that (and the GUI button example above leaves much to be desired), e.g. you want ways to get periodic updates, etc., but it can all be done easily in that framework.
Also you don't have to have a separate instance of Calculation per calculation; you could use setters and such too, and do things like:
class Calculation {
final int input1;
final int input2;
boolean running;
Calculation (int input1, int input2) {
setInput1(input1);
setInput2(input2);
}
void setInput1 (int input1) {
if (running)
throw new IllegalStateException("Inputs can't be changed while calculation running.");
if (input1 < 0)
throw new IllegalArgumentException("Input 1 can't be negative.");
this.input1 = input1;
}
void setInput2 (int input2) {
if (running)
throw new IllegalStateException("Inputs can't be changed while calculation running.");
if (input2 < 0)
throw new IllegalArgumentException("Input 2 can't be negative.");
this.input2 = input2;
}
void begin () {
if (running)
throw new IllegalStateException("Calculation is already running.");
running = true;
...
}
}

Issue with updating a JFrame dynamically

I am using NetNeans
I have a JPanel that is supposed to show some already input data along with a progress bar while the remainder of the program carries out some lengthy processing (about 1 minute long). Once the processing is done, this same JPanel is supposed to be updated with a few JTextFields containing the processed data.
The issue:
The JPanel shows up completely blank throughout the time period the processing is going on. Once the processing is done, then all the data shows up. Its baffling me. Here is how i've programmed it:
/*The parent class which calls the output JPanel
*Lister is the output JPanel. The constructor calls initComponents(), and fills
*text fields that contain the data that has already been input.
*fillresult then triggers the processing function that takes a while. Once the
*processing function is done, fillresult fills the processed data into the
*respective Text Fields
*/
ListWords = new Lister();
System.out.println("l");
ListWords.setVisible(true);
ListWords.fillResult();
//Lister constructor:
Lister(){
initComponents();
MatchWords = new Matcher();//Matcher() only contains the initialization of a List
jTextField1.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(0, 1));
jTextField2.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(1, 2));
jTextField3.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(2, 3));
jTextField4.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(3, 4));
jTextField5.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(4, 5));
jTextField6.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(5, 6));
jTextField7.setText(Scrabbulous.scrab.EntryWind.getCombo().substring(6));
}
//fillresult():
public void fillResult(){
int rIndex = 0;
MatchWords.makeWords(rIndex);
while(MatchWords.realWords.Head == null && rIndex < 2){
rIndex++;
updateStatusBar();
MatchWords.makeWords(rIndex);
}
Wordpoints = new String[MatchWords.realWords.getSize()];
for(int i=0; i<Wordpoints.length; i++){
Wordpoints[i] = "";
}
for(int i=0; i<MatchWords.realWords.getSize(); i++){
int total = 0;
for(int j=0; j<MatchWords.realWords.getNode(i).getWord().length(); j++){
for(int k=0; k<Scrabbulous.scrab.scralphabet.length; k++){
if(MatchWords.realWords.getNode(i).getWord().charAt(j) == Scrabbulous.scrab.scralphabet[k].getLetter()){
total += Scrabbulous.scrab.scralphabet[k].getValue();
Wordpoints[i] = ""+total;
}
}
}
}
try{
jTextField8.setText(MatchWords.realWords.Head.getWord());
jTextField13.setText(Wordpoints[0]);
}catch(NullPointerException e){
jTextField8.setText("No Match Found");
jTextField13.setText("-");
}
try{
jTextField9.setText(MatchWords.realWords.getNode(1).getWord());
jTextField14.setText(Wordpoints[1]);
}catch(NullPointerException e){
jTextField9.setText("No Match Found");
jTextField14.setText("-");
}
try{
jTextField10.setText(MatchWords.realWords.getNode(2).getWord());
jTextField15.setText(Wordpoints[2]);
}catch(NullPointerException e){
jTextField10.setText("No Match Found");
jTextField15.setText("-");
}
try{
jTextField11.setText(MatchWords.realWords.getNode(3).getWord());
jTextField16.setText(Wordpoints[3]);
}catch(NullPointerException e){
jTextField11.setText("No Match Found");
jTextField16.setText("-");
}
try{
jTextField12.setText(MatchWords.realWords.getNode(4).getWord());
jTextField17.setText(Wordpoints[4]);
}catch(NullPointerException e){
jTextField12.setText("No Match Found");
jTextField17.setText("-");
}
Could someone please be of assistance? Oh yes, and I read about adding a .refactor() and/or repaint(), but atleast the jForms initialized in the constructor should show up? :/
Also, it might be worth mentioning that all the code that is currently in the fillresult() function was initially in the constructor, and the output was displaying. However, since I want the progressbar here, I've shifted it to a new function. help please?
"The issue: The JPanel shows up completely blank throughout the time period the processing is going on. Once the processing is done, then all the data shows up."
What's happening is that everything is occurring on one thread, the Event Dispatch Thread (EDT). In order for an event to occur, like your JPanel updating, the process before it needs to complete.
As stated about the EDT:
" Tasks on the event dispatch thread must finish quickly; if they don't, unhandled events back up and the user interface becomes unresponsive."
To work around this, you'll want to run your long process in a background thread. You should have look at Concurrency in Swing - the section on Worker Threads and SwingWorker in particular.

Categories