简体   繁体   中英

MultiThreading locking up Java Swing GUI

I'm working on a project for a multiple user paint program and I have run into a problem with my GUI locking up.

I have most of the program done except for this error, it is a rather large program so I tried to reproduce the error in a smaller program.

In this smaller program, I have 2 JFrames. Both of them can be drawn in by clicking and dragging the mouse. The secondary JFrame is a thread that sleeps for 10 seconds and then sends what you have drawn to the other frame to be displayed.

However, once the main frame has received the image from the secondary frame, the GUI locks up and the main frame can no longer be drawn in.

I'm currently using the SwingUtilities.invokeLater() method to work with. While looking for answers, I found the SwingWorker class, but I wanted to see if there was a simple solution before I undertook a large rewrite of my code to try to make it work with SwingWorker.

Thanks for reading. My code is below. Also, this is my first time posting here. I seemed to be having some trouble formatting the code, so I apologize in advance if it comes out wrong. I will do my best to fix it.

package gui_thread_test;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;

public class DrawPanel extends JComponent
{
    private Image canvas;
    private int x, y, prevX, prevY;
    Graphics2D g2;

    public DrawPanel()
    {
        setDoubleBuffered(false);
        addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e )
            {
                prevX = e.getX();
                prevY = e.getY();
            }
        });

        addMouseMotionListener(new MouseMotionAdapter()
        {
            @Override
            public void mouseDragged(MouseEvent e)
            {
                x = e.getX();
                y = e.getY();
                g2.drawLine(prevX, prevY, x, y);
                repaint();
                prevX = x;
                prevY = y;
            }
        });
    }

    @Override
    public void paintComponent(Graphics g)
    {
        if (canvas == null)
        {
            canvas = createImage(getSize().width, getSize().height);
            g2 = (Graphics2D) canvas.getGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        }
        g.drawImage(canvas, 0, 0, null);
    }

    public synchronized void updateCanvas(Image _canvas)
    {
        canvas = _canvas;
        repaint();
    }

    public Image getImage()
    {
        return canvas;
    }
}

-

package gui_thread_test;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ThreadTest extends JFrame implements Runnable
{
    private Image canvas;
    private Graphics2D g2;
    private DrawPanel panel;
    private DrawPanel threadPanel;

    public ThreadTest(DrawPanel _panel)
    {
        super("Secondary");
        panel = _panel;

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setPreferredSize(new Dimension(500,500));
        this.setMinimumSize(new Dimension(500,500));
        this.setVisible(true);

        threadPanel = new DrawPanel();
        this.add(threadPanel);
    }


    public void paintComponent(Graphics g)
    {
        if (canvas == null)
        {
            canvas = createImage(getSize().width, getSize().height);
            g2 = (Graphics2D) canvas.getGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }
        g.drawImage(canvas, 0, 0, null);        
    }

    @Override
    public void run() {
        //Sleep thread for 10 seconds to give time to draw on the image
        try {
            Thread.sleep(10000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
        }

        SwingUtilities.invokeLater(
                new Runnable() {
                    @Override
                    public void run() {
                        //Posts the message to the server chat window
                        panel.updateCanvas(threadPanel.getImage());
                    }
                });
    }

}

-

package gui_thread_test;

import java.awt.Dimension;
import javax.swing.JFrame;


public class GUI_Thread_Test {

    public static void main(String[] args) 
    {
        JFrame window = new JFrame("Main");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setPreferredSize(new Dimension(500,500));
        window.setMinimumSize(new Dimension(500,500));

        DrawPanel draw = new DrawPanel();
        window.add(draw);

        Thread testThread = new Thread(new ThreadTest(draw));
        testThread.start();


        window.pack();
        window.setVisible(true);
    }

}

Use a Swing Timer rather than a Thread for your every 10 second transfer. Use BufferedImages to hold the drawings and to transfer the drawings.

For example:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class GuiDoubleDraw extends JPanel {
   private static final int BI_WIDTH = 500;
   private static final int BI_HEIGHT = BI_WIDTH;
   public static final Color PEN_COLOR = Color.black;
   private BufferedImage backgroundImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
         BufferedImage.TYPE_INT_ARGB);

   public GuiDoubleDraw() {
      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      addMouseListener(mouseAdapter);
      addMouseMotionListener(mouseAdapter);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (backgroundImg != null) {
         g.drawImage(backgroundImg, 0, 0, this);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(BI_WIDTH, BI_HEIGHT);
   }

   public BufferedImage getBackgroundImage() {
      BufferedImage copyImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
            BufferedImage.TYPE_INT_ARGB);
      Graphics g = copyImg.getGraphics();
      g.drawImage(backgroundImg, 0, 0, null);
      g.dispose();
      return copyImg;
   }

   public void setBackgroundImage(BufferedImage bImg) {
      this.backgroundImg = bImg;
      repaint();
   }

   private class MyMouseAdapter extends MouseAdapter {
      Point previousPt = null;

      @Override
      public void mousePressed(MouseEvent mEvt) {
         previousPt = mEvt.getPoint();
      }

      @Override
      public void mouseDragged(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      @Override
      public void mouseReleased(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      private void drawPt(MouseEvent mEvt) {
         Graphics g = backgroundImg.getGraphics();
         Point nextPt = mEvt.getPoint();
         g.setColor(PEN_COLOR);
         g.drawLine(previousPt.x, previousPt.y, nextPt.x, nextPt.y);
         g.dispose();
         previousPt = nextPt;
         repaint();
      }

   }

   private static void createAndShowGui() {
      final GuiDoubleDraw guiDoubleDraw1 = new GuiDoubleDraw();
      final GuiDoubleDraw guiDoubleDraw2 = new GuiDoubleDraw();

      JFrame frame = new JFrame("Draw 1");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(guiDoubleDraw1);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      JDialog dialog = new JDialog(frame, "Draw 2", ModalityType.MODELESS);
      dialog.getContentPane().add(guiDoubleDraw2);
      dialog.pack();
      dialog.setLocationRelativeTo(null);
      dialog.setVisible(true);

      int timerDelay = 10 * 1000;
      new Timer(timerDelay, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            guiDoubleDraw1.setBackgroundImage(guiDoubleDraw2
                  .getBackgroundImage());
         }
      }).start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Your application isn't locking up, your Thread is exiting.

There's nothing here to stop the run method from completing, there by terminating the Thread , which means there will not be any more updates.

public void run() {
    //Sleep thread for 10 seconds to give time to draw on the image
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
    }

    SwingUtilities.invokeLater(
            new Runnable() {
                @Override
                public void run() {
                    System.out.println("Post...");
                    //Posts the message to the server chat window
                    panel.updateCanvas(threadPanel.getImage());
                }
            });
}

HovercraftFullOfEels is correct, use javax.swing.Timer instead. It will continue ticking at a regular interval.

You will want to implement the SwingWorker in your code which will allow you to run code on and off the Event Dispatch Thread. Even if you should find a work around in this scenario, SwingWorker is the proper way to use threads to update the UI. You will find that working with various threads and trying to update the UI without SwingWorker will usually "paint yourself into a corner." I would refactor and use this as a positive learning experience.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM