When I use setSelectedComponent or setSelectedIndex on a JTabbedPane object, the panel always comes up in my UI. However, sometimes the tab associated with the panel remains hidden. In other words, the tab does not scroll to a visible portion of the tabbed pane.
How can I fix this? I have tried the cheesy select one index, then select desired index, as well as several other more elegant things, but arrrrgh!!
Help me if you can.
Thanks,
Todd
I think your call is not done on EDT. Wrap it with SwingUtilities.invokeLater
Here is a patter you can use if you have a method that alters swing components, or their models and so must be called on the EDT, but may be called from a background thread. This ensures func always runs on the EDT:
void func(final Type1 arg1, final Type2 arg2) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
func(arg1, arg2);
}
});
return;
}
// method code goes here
}
Related
I'm developing a cli-based custom web crawler in Java. Since it needs to have a user-friendly graphical interface for showing the result, I should add a swing frame to that involving some trees, labels, tables and so on.
That graphical interface is one of its switches, and must be started just in case user calls it. Thus, I have to start this interface in a new thread so that the application can proceed with other tasks and update components inside of GUI frame if needs.
My GUI class is some thing like:
public class Frame extends JFrame {
......
public static JLabel urlLabel;
......
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
And, I fork it from my main class like this:
.....
if(cmd.gui){
Frame.run();
Frame.urlLabel.setText("New Url");
}
......
Unfortunately, the text of label doesn't change. I already tested repaint(), revalidate() and such other functions like these, but, nothing turned up.
I tested getText() in order to make sure it is possible to access urlLabel from main class, and it worked (I could retrieved text of label).
I was wondering how I can sort out this issue? (Basically, I need to start a swing frame in a different thread and control its components from the main thread)
Thanks in advance.
If you use invokeLater(), your Runnable will be started in the EventThread after the current operation in this thread is finished. If your label is not updated, it might be that your EventThread is busy doing something else - e.g. crawling the web.
You definitely need to make sure that your crawling work is done in another thread (start a new one, don't use the one that runs anyway, since this is most probably the EventThread). Then you might use invokeLater() in this other Thread to change the label in the EventThread.
Hint: You can check if you are in the EventThread by using SwingUtilities.isEventDispatchThread().
Remember that your data/models will be used by different threads and that this might cause problems when the data is changed in your worker thread while your GUI is trying to display it.
Thank you guys for helping.
Finally, I could overcome this obstacle by using SwingUtilities.invokeLater for updating the label's text.
I mention the approach here, since someone else might need it:
The main class:
public class Frame extends JFrame {
......
private static JLabel urlLabel;
......
public JLabel getU(){
return urlLabel;
}
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
The GUI class:
if(cmd.gui){
Frame().run();
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
gui.getU().setText("New Url");
}
});
}
.....
Just a question about this manner:
Since I need to update some labels and tree nodes a couple of times during crawling, wanted to know if starting a new Runnable for each of those would be overload? If yes, how can I manage that?
---UPDATE---
According to the #xander's comment, it is possible to use lambda instead of Runnable. I think lambda doesn't have overload as much as a new object does.
My users like having multiple JFrames; it allows them to resize the different components and place them wherever they want on the screen. However, I have a request to make all the child windows come to the front together... in other words, lets say they maximize another window in front of all the windows, and then use the task bar to click on just one of the JFrames. How can I set it so that they all come to the front? Note: it is also possible to close the child windows; if they are actually hidden, I do not want them to come to the front. I have a class ApplicationModel that keeps track of whether a window is hidden or not.
Things I've tried:
Using windowActivated() and focusGained() to try to bring them all to the front. This usually results in an infinite loop. The problem is that my eventing framework sends these requests off the Event Dispatch Thread, so any sort of blocking with an AtomicBoolean doesn't last long enough.
The main problem is not that I can't make them come to the front... I have made them come to the front. The problem is that they KEEP trying to come to the front, as bringing a window to the front throws the focusGained and windowActivated events, which creates an endless loop...
Making one window the master, and making the others a JDialog. Unfortunately, either the windows are modeless (and therefore don't come to front with the master window), or they are modal, (and therefore block the master window).
How can I fix either of these problems, or is there an entirely different third solution?
You can use a boolean field as a flag to prevent the infinite loop:
private boolean movingAllFramesToFront;
public void windowActivated(WindowEvent event) {
if (movingAllFramesToFront) {
return;
}
movingAllFramesToFront = true;
List<Frame> frames = getAllApplicationFrames();
for (Frame frame : frames) {
if (!applicationModel.isHidden(frame)) {
frame.toFront();
}
}
event.getWindow().toFront();
event.getWindow().requestFocus();
EventQueue.invokeLater(new Runnable() {
public void run() {
movingAllFramesToFront = false;
}
);
}
Another thing you can try is the new autoRequestFocus property introduced in Java 1.7. I have never tried using it, but here's my understanding of how it works:
public void windowActivated(WindowEvent event) {
final List<Frame> frames = getAllApplicationFrames();
for (Frame frame : frames) {
if (!applicationModel.isHidden(frame)) {
frame.setAutoRequestFocus(false);
frame.toFront();
}
}
EventQueue.invokeLater(new Runnable() {
public void run() {
for (Frame frame : frames) {
if (!applicationModel.isHidden(frame)) {
frame.setAutoRequestFocus(true);
}
}
}
);
}
I have an application with a lot of windows and had a problem similar to yours. My workaround is:
#Override
public void windowActivated(WindowEvent e) {
if (e.getOppositeWindow() == null) {
//front every window
}
}
First I created a class "SlveFrame" (Slve being the name of my app), a child of "JFrame".
public class SlveFrame extends JFrame implements WindowListener {
static ArrayList<SlveFrame> frames = new ArrayList<SlveFrame>();
public SlveFrame () {
addWindowListener(this); / /to make JFrame fire WindowListener's method
}
/ /... every method added from WindowListener
#Override
public void windowActivated(WindowEvent e) {
if (e.getOppositeWindow() == null) { // return null if window is not from my (or Your) work
for (SlveFrame frame : frames) { // if you have no idea what this is, look for "for each loop java" in google
frame.toFront();
}
}
}
/**
* The use of SlveFrame is almost the same as Jframe
*/
#Override
public void setVisible (boolean b) {
if (b)
frames.add(this);
else
frames.remove(this); // may raise an exception if you're not careful
super.setVisible(b); // or your window will simply not be visible.
}
#Override
public void dispose () {
frames.dispose(this) // may raise an exception you'll want to handle
}
}
The trick being that WindowEvent.getOppositeWIndow() returns a Jframe if the JFrame (or child class) is from your own program, meaning that if you switch to another program or app (such as eclipse, Firefox or a text editor) then back to any of your windows, then a call to getOppositeWindow() will return a 'null'. A simple if (e.getOppositeWindow()) makes it fairly easy to determine whether your window gain focus in condition that would require you to bring every window to the front, or rather to let everything be.
The overriding of setVisible (boolean b) and dispose () are optional but allow the dev to use it as a regular window.
I hope i could be of some help. Sincerly ~a lama
I have two separate components, one is a class that extends JTable, the other is a class that extends JPanel (contains a form).
Both are displayed on the same view when that application is run, when I click on a row on the table, I expect the textfileds on the form to be updated but nothing happens
I use observer (the form class is the listener) pattern to send the clicked row to the class/ panel containing the form fields to be updated, the values are received but the textfields are not updated.
The code below is in the form class and it updates the form fields, the form class is added as a listener in the table class, the method below is fired when a table row is clicked
public void onTableRowClick(CollectedParcelEvent e)
{
JOptionPane.showMessageDialog(null,"test", "test", 1);
txtCost.setText(Double.toString(e.getSource().getCost()));
txtCustomerName.setText(e.getSource().getCustomer().getName());
txtCost.repaint();
txtCost.revalidate();
}
public void onTableRowClick(CollectedParcelEvent e)
{
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
//JOptionPane.showMessageDialog(null,"test", "test", 1);
txtCost.setText(Double.toString(e.getSource().getCost()));
txtCustomerName.setText(e.getSource().getCustomer().getName());
}
});
}
Events are handled on the single event thread. There the GUI is not responsive for other events, and one should postpone doing such things later with invokeLater.
I don't understand why are you calling revalidate(); as its just tells the layout manager to reset based on the newly added or removed component list.
See this link for more answers about using revalidate(); and also this one
And perhaps, repaint(); should be enough for the required change.
So check your method to see if it really gets fired or not.
I'm trying to listen for a window close event on the parent JFrame of a JPanel. In the WindowClosing event I'd like to de-register a listener to a different component.
Unfortunately the only code I can gaurantee to have run is the constructor for the panel. What this means is that the panel itself doesn't have an ancestor window yet, so simply calling SwingUtilities.getWindowAncestor doesn't work. So what I do is register a hierarchy listener, and in the hierarchyChanged event look for SHOWING_CHANGED event. When that even fires, now I can look for the window ancestor of the panel.
So basically I have the following:
class ShapeControlPanel extends JPanel{
public ShapeControlPanel(){
final ShapeControlPanel me = this;
me.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) == HierarchyEvent.SHOWING_CHANGED){
SwingUtilities.getWindowAncestor(me).addWindowListener(new WindowListener() {
/* Snipped some empty handlers */
#Override
public void windowClosing(WindowEvent e) {
/* Finally get to remove the handler. */
me.getApparent().removeLocationSelectionListener(me.GUID(), me);
}
});
}
}
});
}
}
Is this sane? Is there a more reasonable way of getting a handle on the frame closing event?
It's not the ugliest thing I've seen (I wouldn't even say it's all that bad), but you have to ask yourself: why does your panel really need to know when the window is closed? It seems to be an odd coupling that would best be removed.
I don't know enough about your context and what you are truly trying to accomplish to suggest an alternative right now. But if a panel needs to know about the container in which it resides, there is probably some bad design with harmful coupling.
I have a component (JPanel) inside a Window.
I always get false when calling panel.isShowing(),
when calling from a windowGainedFocus() event (when the parent window gets focus).
I assume that when the windowGainedFocus() event is called, the painting of the JPanel within this Window had not been finished yet.
I was trying to place that call isShowing() on the paint() method of this Window,
but I always get isShowing() = false.
Is there a way I could get an event when the JPanel is fully shown on screen and the isShowing() method will return true ?
Thanks
You should probably best approach this with a hierarchy listener on the panel itself:
panel.addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
if ((HierarchyEvent.SHOWING_CHANGED & e.getChangeFlags()) !=0
&& panel.isShowing()) {
//do stuff
}
}
});
If you don't want an event but have some specific code that needs to be run after your component has been drawn, you can override addNotify(), which gets called to make the component displayable. Example:
public void addNotify()
{
super.addNotify();
// at this point component has been displayed
// do stuff
}
You component will be fully displayed after you receive WindowListener.windowActivated. You will also run into timing problems and race conditions trying to assign focus before the windowActivated event.