2013-04-05 100 views
2

我正在为多用户绘图程序开发一个项目,并且遇到了一个问题,我的GUI已锁定。多线程锁定Java Swing GUI

我已经完成了大部分的程序,除了这个错误,它是一个相当大的程序,所以我试图在较小的程序中重现错误。

在这个较小的程序中,我有2个JFrame。他们都可以通过点击并拖动鼠标来绘制。辅助JFrame是一个睡眠10秒的线程,然后将你绘制的内容发送到另一个要显示的帧。

然而,一旦在主框架已经接收到图像从所述第二框架,所述GUI锁死和主框架不再能在绘制。

我目前使用的SwingUtilities.invokeLater()方法跟...共事。在寻找答案时,我找到了SwingWorker类,但我想先看看是否有一个简单的解决方案,然后再对代码进行大量重写,以使其与SwingWorker一起工作。

感谢您的阅读。我的代码如下。此外,这是我第一次在这里发布。我似乎在格式化代码时遇到了一些麻烦,所以如果它出错,我会提前道歉。我会尽我所能解决它。

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); 
    } 

} 

回答

2

使用Swing的计时器,而不是你的每10第二转移线程。使用BufferedImages保存图纸并传输图纸。

例如:

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(); 
     } 
     }); 
    } 
} 
+0

传输实际上是通过我的流套接字连接来的。我只是使用线程睡眠作为例子来重现我的锁定错误。但是,我检查了你的代码,它似乎解决了这个问题。我现在要更深入地研究它。我想我应该能够弄清楚我的问题。也许不使用BufferedImages是我出错的地方。谢谢。 – Lucas 2013-04-05 04:16:33

1

你将要实现在代码中的SwingWorker这将允许你打开和关闭事件指派线程运行的代码。即使您在这种情况下应该找到解决方法,SwingWorker是使用线程更新UI的正确方法。你会发现,使用各种线程并尝试在没有SwingWorker的情况下更新UI通常会“让自己陷入一个角落”。我会重构并将其用作积极的学习体验。

+0

尽管“技术上”没有错,但javax.swing.Timer会更适合手头的问题 - 定期更新...恕我直言(更不用说更容易了) – MadProgrammer 2013-04-05 03:24:25

+0

我打算重构它,以获得一些使用SwingWorker的经验,现在我已经找到了它,但是我必须在我有时间学习SwingWorker之前展示一些工作代码,但重构在我尝试向我的完整程序添加更多功能之前,SwingWorker绝对是我的工作清单。 – Lucas 2013-04-05 04:12:57

2

您的应用程序未锁定,您的Thread正在退出。

这里没有什么可以阻止run方法完成,通过终止Thread,这意味着将不会有任何更新。

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是正确的,请使用javax.swing.Timer来代替。它会继续定时滴答滴答。

+0

事实上,这是事实上显示原始海报的代码有问题的答案。 1+ – 2013-04-05 03:41:31

+0

线程正是我用来重现错误。问题是你可以在主框架中绘制。二级框架将图像发送到主框架,主框架显示它。现在,主框架无法绘制。 – Lucas 2013-04-05 04:05:23