I have the following Java Program which one starts in about 50% of all launch attempts. The rest of the time it seams to deadlock in the background without displaying any GUI. I traced the problem to the setText method of the JTextArea Object. Using another Class like JButton works with setText but JTextArea deadlocks. Can anyone explain to me why this is happening and what is wrong with the following code:
public class TestDeadlock extends JPanel {
private JTextArea text;
TestDeadlock(){
text = new JTextArea("Test");
add(text);
updateGui();
}
public static void main(String[] args){
JFrame window = new JFrame();
window.setTitle("Deadlock");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new TestDeadlock());
window.pack();
window.setVisible(true);
}
public synchronized void updateGui(){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
System.out.println("Here");
text.setText("Works");
System.out.println("Not Here");
}
});
}
}
your main method must be wrapped into invokeLater or invokeAndWait, that's basic Swing rule to create Swing GUI on EventDispashThread
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame window = new JFrame();
window.setTitle("Deadlock");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new TestDeadlock());
window.pack();
window.setVisible(true);
}
});
}
Related
I am trying to make a Swing GUI that includes some 3D stuff using Java3D's Canvas3D object. The problem is that it takes a while for a Canvas3D object to initialize, and I want the Swing GUI to come up right away. My solution to this problem is to initialize the Canvas3D in a separate thread, and then add it to the JFrame once it is initialized. However, when that separate thread adds the Canvas3D to the JFrame, the window loses focus for a moment, which is undesirable. How can I prevent that from happening? I have included a simple example to illustrate what I am trying to do:
public class Main extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::setup);
}
private void setup() {
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
Thread thread = new Thread() {
#Override
public void run() {
Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
SwingUtilities.invokeLater(() -> {
Main.this.add(canvas); //the window loses focus for a moment here
Main.this.revalidate();
});
}
};
thread.start();
}
}
I am using Java3D 1.7.1.
I have modified my code as per R VISHAL's comment, but the problem still persists.
public class Main extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main()::setup);
}
private void setup() {
setSize(600, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
SwingWorker<Canvas3D, Object> worker = new SwingWorker<Canvas3D, Object>() {
#Override
public Canvas3D doInBackground() {
return new Canvas3D(SimpleUniverse.getPreferredConfiguration());
}
#Override
public void done() {
try {
Main.this.add(get());
} catch (InterruptedException|ExecutionException e) {
throw new RuntimeException();
}
Main.this.requestFocusInWindow();
Main.this.revalidate();
}
};
worker.execute();
}
}
Ok so this might not be the answer but it was to large to put it as an comment
Use a JPanel having a CardLayout as your frame's contentpane, have one screen inside this panel set as the background[or whatever initial screen you want to display before the canvas is displayed], and then once the canvas is initialized add it to the content pane as the second screen and then call the CardLayout's show() method do display the canvas
public class Add
{
public static void main(String args[])
{
JFrame frame=new JFrame("Test");
JPanel mainPanel=new JPanel(new CardLayout());
mainPanel.addMouseListener(new MouseAdapter()
{
int count=0;
#Override
public void mouseClicked(MouseEvent m)
{
System.out.println("Focus "+(++count));
}
});
JPanel background=new JPanel();
background.setBackground(Color.WHITE);
mainPanel.add("Screen1",background); //Initial Screen To Show Something To The User While Canvas Is Being Initialized
frame.setContentPane(mainPanel);
SwingWorker worker=new SwingWorker<Canvas3D,Object>()
{
#Override
public Canvas3D doInBackground(){return new Canvas3D();}
#Override
public void done()
{
try
{
mainPanel.add("Screen2",get()); //Add Canvas To MainPanel
CardLayout layout=(CardLayout)mainPanel.getLayout();
layout.show(mainPanel,"Screen2"); //Remember This While Using CardLayout
}
catch(InterruptedException | ExecutionException ex){}
}
};
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
worker.execute();
}
private static final class Canvas3D extends JPanel
{
private Canvas3D()
{
super(new BorderLayout());
try{Thread.sleep(5000);}//Mimicing Long Operations
catch(Exception ex){}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0,0,500,500);
g.setFont(new Font("",Font.BOLD,15));
g.setColor(Color.RED);
g.drawString("CANVAS 3D",100,100);
}
}
}
Of course you would use your actual canvas3d instead of this custom one and there is no need to revalidate or requestFocus()
If you are still worried about focus you could always create an javax.swing.Timer class to request focus to your mainPanel or frame[Or Both See What Works For You] every second/or millisecond
Timer timer=new Timer(1000,new ActionListener() //milliseconds make it 1 if your are dead serious about focus
{
#Override
public void actionPerformed(ActionEvent e)
{
mainPanel.requestFocusInWindow();
frame.requestFocusInWindow(); //May not be required since we are already requesting focus in mainPanel
}
});
timer.start();
If you want to get even more paranoid about focus you could always add an focus listener
mainPanel.addFocusListener(new FocusListener()
{
#Override
public void focusLost(FocusEvent e)
{
mainPanel.requestFocusInWindow();
frame.requestFocusInWindow();
}
#Override
public void focusGained(FocusEvent e){}
});
If any of these suggestions did/didn't work comment below :)
I want to open MainWindow (that I created and added UI) after some time but Java opens blank default Jframe instead. How can I open already created window after splashscreen (Start)?
public class Start extends JFrame{
private JPanel panel1;
public static void main(String[] args) {
JFrame frame = new JFrame("Starting");
frame.setContentPane(new Start().panel1);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setExtendedState(JFrame.NORMAL);
frame.setUndecorated(true);
//frame.setAlwaysOnTop(true);
frame.setVisible(true);
/*Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation(dim.width/2-frame.getSize().width/2, dim.height/2-frame.getSize().height/2);*/
frame.toFront();
frame.pack();
frame.setLocationRelativeTo(null);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new MainWindows().setVisible(true);
frame.setVisible(false);
//System.exit(0);
}
});
timer.start();
}
MainWindows.java
public class MainWindows extends JFrame{
private JPanel panel;
public static void main(String[] args)
{
boolean clicked = false;
JOptionPane.showConfirmDialog(null, "Це перша вершія гри. Будь ласка, закрийте всі програми, щоб уникнути помилок.", "Увага!", JOptionPane.YES_OPTION);
JFrame frame2 = new JFrame("Flying");
frame2.setContentPane(new MainWindows().panel);
frame2.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame2.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame2.setUndecorated(true);
frame2.setAlwaysOnTop(true);
frame2.setVisible(true);
frame2.toFront();
frame2.pack();
Your MainWindow class has a static main method that's never invoked. It looks a lot like the code in there should have gone intoa constructor instead.
You seem to have two main methods in this program. I would suggest removing the main method from MainWindows.java and move the code from it into the MainWindows constructor. This will ensure that when you initialize the new MainWindows() object, everything in the new JFrame should be set up correctly.
Code in MainWindows.java:
public MainWindows()
{
boolean clicked = false;
JOptionPane.showConfirmDialog(null, "Це перша вершія гри. Будь ласка, закрийте всі програми, щоб уникнути помилок.", "Увага!", JOptionPane.YES_OPTION);
JFrame frame2 = new JFrame("Flying");
frame2.setContentPane(new MainWindows().panel);
frame2.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame2.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame2.setUndecorated(true);
frame2.setAlwaysOnTop(true);
frame2.setVisible(true);
frame2.toFront();
frame2.pack();
I finally fixed it. frame2.setContentPane(panel); Thank you for all your answers!
I have an undecorated ( setUndecorated(true) ) frame which doesn't behave properly when pressing windows key + right/left arrow keys.
Please refer the sample code bellow :
import javax.swing.*;
import java.awt.*;
public class TestGUI {
private static void createAndShowGUI() {
JFrame frame = new JFrame("HelloWorldSwing") {
#Override
public boolean isUndecorated() {
return super.isUndecorated();
}
};
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JLabel label = new JLabel("Hello World");
frame.getContentPane().add(label);
frame.setPreferredSize(new Dimension(400, 400));
frame.setUndecorated(true);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
But when I set the frame as setUndecorated(false), then it works properly. I couldn't find any java bug related to this issue. Is there a work around which I could use to get the same behavior with setUndecorated(true)?
I have copied most of my Code from an oracle-example, so I think at least the code I did not add should be correct and I don´t like to change that. But in the oracle-code I can not implement this line to properly close my JFrame: frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
private static void createGUI() {
JFrame frame = new JFrame("NameChooser");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
...
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
}
});
}
public void actionPerformed(ActionEvent e) {
if (e.getSource()==skipButton){
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); WindowEvent.WINDOW_CLOSING)); // does not work ofc
}
}
How can I close my JFrame in the actionPerformed-method without destroying this correct way of opening a JFrame ?
Or is this oracle-code just suitable for examples and not for real applications ?
You should make frame an instance field like this:
private static JFrame frame;
private static void createGUI()
{
frame = new JFrame( "NameChooser" );
...
}
given the following code:
public class MainFrame extends JFrame{
public MainFrame() throws HeadlessException {
super();
this.setSize(500, 400);
this.setVisible(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JButton myButton = new JButton("Test");
this.add(myButton);
this.pack();
}
public static void main(String[] args) {
new MainFrame();
}
}
Does the code inside the constructor run on the EDT. I think it does because it's executed "inside" an instance of a JFrame, but I need a second opinion.
Continuing the idea, If I were to create other controls, for example in the main() function, that code wouldn't be on the EDT?
Thank you!
No. You are calling the constructor from the main method which runs on the main thread.
Add the usual boilerplate:
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
new MainFrame();
}});
}
Also it's generally a bad idea to extend classes that you don't need to (including JFrame, JPanel and Thread). There is no need to declare HeadlessException as it is unchecked.