I want to write a program, which search some HTML addresses. I assume that one search will be going more than 1 minute. When I print the results to console everything is OK, but when I make a frame the results don't appear in textpane. Now I have two classes one to search and one for frame. I write the simply function in the frame class to check if adding text working:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
String Txt=(String)jComboBox1.getSelectedItem();
jTextPane1.setText("");
addText(Txt);
SecondClass.find(Txt);
}
public void addText(String text){
StyledDocument doc = jTextPane1.getStyledDocument();
try{
doc.insertString(doc.getLength(), text, null);
}catch(Exception e) { System.out.println(e); }
}
And in SecondClass I write the same line to addText, but it is working only from frame class. The second problem is that if the search is going I cannot do anything in program and the text from frame class is see after search is finished. I want see text immediately after finding it like in console which is working and I want to have possibility to click this link before search is ended ( I don't implement clickable links yet and don't know how to do this). I think that I must synchronize the processes, but I don't know how to do this.
It sounds like you are doing the main downloading task on the special Swing event thread, which handles all the work for Swing components. This will prevent the GUI from responding properly.
This will happen 'by accident' if you do long-running actions within event handlers (e.g. when you press a JButton).
You need to do long-running tasks in a separate thread, then 'hand over' the data to the Swing thread to update the GUI, using SwingUtilities.invokeLater(runnable); Edited: or see Max's answer about SwingWorker.
For example, your worker thread would do something like this when it has some data ready, using an anonymous Runnable:
final String text = getText();
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
appendText(text);
}
});
where getText() just indicates some means of acquiring the downloaded text, and appendtext() is a method you will need to write to update your textpane.
Check out SwingWorker. It should help you with handling lengthy operation and managing intermediate results as well. There is a sample in this manual that illustrates use of publish() and process() methods to append intermediate text results to JTextArea.
Related
I know many people have asked this question before but I couldn't find any answer that solved my problem. My code is like this:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
myfunction(); //runs for a very long time
}
The original text of JLabel is "OFF". Now I want to change the text to "ON" when the mouse is clicked but the text doesn't set until myfunction() is complete (which may take several minutes).
I have tried the invalidate function, making a separate function for setting the text but nothing is working.
Please help me with this problem!
The problem is that mouseClicked(...) is executed on the UI Thread. That is the Thread that is responsible for handling all sorts of user actions (like a mouse click) and also the drawing of components (like updating the text of the label on screen). If you execute your long running method call on the UI thread, it will be blocked and can't draw anything until execution is complete. You'll have to use multi threading to get around this problem.
The following might not be the most elegant solution, but if you are new to multi threading it will get the job done:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
(new Thread() {
public void run() {
myfunction();
}
}).start();
}
It will spawn a new Thread that handles your method, which will let the UI Thread continue doing its thing. consider deactivating the button that has just been clicked, so the user can't start the execution while it is already in progress (which usually is what you want..)
All processors that update my swing GUI from places other than by user clicks are performed using EventQueue.invokeLater (for example output generated from a long running background non-EDT thread process).
In my current scenario I have a TCPIP socket read background thread process that returns data which needs to update a JEditorPane object. I use the JEditorPane setText call. The problem is, placing the setText call in an invokeLater routine freezes the GUI for large files (example case test 19,790 KB).
My attempt to resolve this is to perform the setText action in a non-EDT background thread. This appears to solve the problem, BUT, I am concerned about best practices, because JEditorPane setText in java 7 (the JDK I’m using) is NOT thread safe.
HOWEVER trawling through the JDK code, it seems to me that the lengthy process performed here is in JDKs DefaultEditorKit.read, and within that method the only code that would effect the GUI is in the doc.insertString calls (unless I am mistaken). Now when you look at JDKs PlainDocument.java insertString method it documents that IT IS thread safe, so one would think therefore that this solution is sound.
HOWEVER...
Stress testing my application, I do some random clicks around the GUI, and currently have a tree node animation running, and during the large load below, it does appear to slow down the animation a little, hence my concern that I’ve not performed the best resolution (also very concerned about future JREs screwing me up here and therefore not relying on insertString currently being threadsafe).
I’ve investigated and seen that this question “how to handle long running JEditorPane setText” has been asked before but with no suitable answers.
QUESTION 1) Does anyone have thoughts on my current observations?
QUESTION 2) Does anyone have ideas on how I could achieve this another?
NOTE JEditorPane is my only choice here because I will be eventually supporting dynamic fonts of an IDE look and feel nature.
NOTE also that the below call is called within a EventQueue.invokeLater routine, so the initial editorPane work is in the EDT.
public void updateBigDataEditorPane( final JEditorPane editorPane, final String inStr ) {
// Update editor object and content.
editorPane.setContentType( "text/plain" );
editorPane.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N
editorPane.setDocument( editorPane.getEditorKit().createDefaultDocument() );
// Content update. NOTE in non-EDT thread to stop GUI freeze with large content.
new Thread( new Runnable() {
#Override
public void run() {
//// synchronized
synchronized( tabsLock ) {
// Set content.
editorPane.setText( inStr );
} //// synchronized
}
}).start();
}
You could use the EditorPane's Document (via editorPane.getDocument()) and perform the changes to this document (via insertString(...), instead of using setText(...)) in another thread.
By doing this, you can (kind of) dynamically (means: while reading) write the contents.
As Example (add text from file to Textpane, without freezing UI):
(EDIT: this new code is not tested, but it should better demonstrate my suggestion...):
public void readFileAsync()
{
final String fileName = "/path/to/file.txt";
final StyledDocument doc = yourTextPane.getStyledDocument();
Runnable r = new Runnable()
{
public void run()
{
try
{
List<String> lines = Files.readAllLines(Paths.get(fileName), Charset.defaultCharset());
for (String line : lines)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
doc.insertString(doc.getLength(), line, null );
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(r);
t.start();
}
While the answers provided were beneficial, and would be used to address specific long running EDT thread requirements, the solution I ended up using was as follows;
Create a new JEditorPane object
Perform the time consuming setText call
On completion, replace currently active JEditorPane object on the GUI with this newly created one.
Use SwingWorker to perform the background non-EDT tasks (1 & 2), and then perform step 3 on completion on the EDT thread.
I'm re doing a specific application, just a basic text editor and I remember I had tabs and a JMenu so if you went File --> New it would add or 'Open' another tab on the JTabbedPane. But this time it's not doing it for me, could someone help? Here is how im doing it:
newFile.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
tabs.addTab("new file", text);
}
}
);
So when it's clicked it should add another tab but it's not for some reason...
If it matters there is a default tab open at the beginning and when you click new it wipes out the old one.
Thanks for any help! (Please ask if you need anymore explanation)
Here I uploaded my code here since the editor here kept saying I the way I was putting it in wasnt formatted correctly:
http://nardcake.com/java
There is 2 files there, one initializes it and the other is everything else
thanks!
try:
tabs.revalidate();
tabs.repaint();
I have removed these two lines (those two are anyhow called in the end by addTab() method), and rewritten your init.java like this:
public static void main(String[] args) {
System.out.println(SwingUtilities.isEventDispatchThread()); // 1
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.out.println(SwingUtilities.isEventDispatchThread()); //2
EBLFWE window = new EBLFWE();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setExtendedState( window.getExtendedState()|JFrame.MAXIMIZED_BOTH );
window.setSize(1024, 728);
window.setVisible(true);
}
});
It works now. To quote myself:
Every usage of Swing components must be done thorugh the Event Dispatch Thread (abbreviated EDT) or you will probably get unwanted visual effects. See here for explanation.
EDIT:
All the GUI related code must be executed on the EDT. You can test if some part of your code is run by EDT like this:
System.out.println(SwingUtilities.isEventDispatchThread());
If it prints true you are safe to do a GUI update (e.g. call methods on Swing components instances) - like in 1 or anywhere in EBLFWE class. However 2 will print false - it is because the thread that runs your program is not EDT.
When calling SwingUtilities.invokeLater() you are actually placing that code to be executed (at some appropriate time the EDT sees fit) in the Event dispatch thread.
EDT does the actual painting, and a lot of other tasks, so when you call GUI update code from another thread you can mess up the order and get unwanted visual apperance.
I have a java class called GameUpdater which extends JInternalFrame.
It used to extend JFrame when I ran the class as a program by itself, but I changed it to JInternalFrame to become part of a larger application - now accessible from a menu button.
The function being called when I press this menu button is as follows:
private void update(){
GameUpdater gu = new GameUpdater();
desktop.add(gu); //add to JDesktopPane
gu.setSize(400, 300);
gu.setVisible(true);
gu.readMatches();//this function takes ages
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
gu.setMatch("DONE"); //now it shows the frame, way too late
}
The method gu.readMatches() takes a long time to execute, so it periodically updates content in the JInternalFrame to display its progress. However the frame is not being shown until this update function is complete!
It's like setVisible(true) is waiting until the end of the function...
It worked absolutely fine when it was a JFrame. Is there any weird property of a JInternalFrame that would cause this?
Cheers
It sounds like you're executing a time consuming process inside the Event Dispatching Thread (EDT), this will prevent the event queue from process (amongst other things) repaint requests.
This will cause you program to appear as if it has "hung".
You need to off load this task to a background thread.
Have a read through Concurrency in Swing, especially the section on Worker Threads and SwingWorker
The problem is that you are blocking your EDT this can be taken care of by simply creating a new Thread/Runnable thar calls gu.readMatches(); the method:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
gu.readMatches(); //gu will have to be declared `final`
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
}
});
ofcourse though you might want to implement a JProgressBar so the user can keep track of of how far the reading is.
Having the following szenario: blurring a textBox (input) writes text to my status-Box (in certain conditions), and clicking a button also writes text to the status-Box.
Now when clicking the button it will blur my textBox if it is focused and that will cause the status-Box to flicker as first the blurHandler will write its result and then the clickHandler.
As i want the result of the clickHandler to appear my idea is to let the blurHandler place an event at the end of the queue which checks whether a clickHandler has written a result before.
In Swing I would try SwingUtilities.invokeLater(Runnable).
The equivalent in GWT is said to be the Scheduler but those deferred or finally commands seems always to run after the current event and before the next.
So far i use Scheduler.scheduleFixedDelay with 100ms delay and hope it comes after the clickHanlder in each browser.
See similar problem with answer.
I think there must be a better solution for this.
How to really add an event to the end of the queue or is it impossible due to limitations of HTML?
Try:
Scheduler.get().scheduleDeferred(new Command() {
#Override
public void execute() {
.......
}
});
See
Class Scheduler