2014-09-19 3 views
0

스윙 작업자, 타이머를 사용하는 데 어려움을 겪고 있습니다. 실제로는 조금 혼란 스럽습니다. 내 이해가 간다면 EDT에서 호출해야하는 반복 작업을 설정하기 위해 타이머를 설정해야합니다. (: https://www.youtube.com/watch?v=kPRA0W1kECg이 같은) GUI가 새로 고쳐지지 이유 난 그냥 이해가 안왜 repaint()가 호출 되더라도 GUI가 업데이트되지 않는 이유는 무엇입니까?

나는 그래픽으로 정렬 alghoritm을 표시하는 프로그램을 만들기 위해 노력하고있어. 주문 된 값을 보여주는 sysout을 넣었으므로 작동하는 것처럼 보이지만 GUI는 변경되지 않습니다.

여기 내 코드입니다 :

public class MainWindow { 
    private JFrame frame; 
    JPanel panel; 
    public final static int JFRAME_WIDTH = 800; 
    public final static int JFRAME_HEIGHT = 600; 
    public final static int NELEM = 40; 

    ArrayList<Double> numbers; 
    ArrayList<myRectangle> drawables = new ArrayList<myRectangle>(); 
    Lock lock = new ReentrantLock(); 
    Condition waitme = lock.newCondition(); 


    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       try { 
        MainWindow window = new MainWindow(); 
        window.frame.setVisible(true); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    public MainWindow() { 
     initialize(); 
    } 

    private void initialize() { 
     frame = new JFrame(); 
     frame.setBounds(100, 100, JFRAME_WIDTH + 20, JFRAME_HEIGHT + 40); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     panel = new myPanel(); 
     frame.getContentPane().add(panel, BorderLayout.CENTER); 

     Timer timer = new Timer(500, new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent arg0) { 
       lock.lock(); 
       try{ 
        //Updating the gui 
        panel.repaint(); 
        panel.revalidate(); 
        //Giving the OK to the sorting alghoritm to proceed. 
        waitme.signal(); 
       }catch(Exception e){ 
        e.printStackTrace(); 
       }finally{ 
        lock.unlock(); 
       } 
      } 
     }); 
     timer.start(); 

     SwingWorker<Integer, String> sw = new SwingWorker<Integer, String>(){ 

      @Override 
      protected Integer doInBackground() throws Exception { 
       mapAndCreate(); 
       bubbleSort(); 
       return null; 
      } 

     }; 
     sw.execute(); 
    } 

    private void bubbleSort() throws InterruptedException{  
     for(int i=0; i < NELEM; i++){ 
      for(int j=1; j < (NELEM-i); j++){ 
       if(drawables.get(j-1).wid > drawables.get(j).wid){ 
        //swap the elements! 
        myRectangle temp = drawables.get(j-1); 
        drawables.set(j-1, drawables.get(j)); 
        drawables.set(j, temp); 
        lock.lock(); 
        try{ 
         //Wait for the GUI to update. 
         waitme.await(); 
        }catch(Exception e){ 
         e.printStackTrace(); 
        }finally{ 
         lock.unlock(); 
        } 
       } 
      } 
     } 

    } 

    /*** 
    * Function that maps values from 0 to 1 into the rectangle width. 
    */ 
    private void mapAndCreate() { 
     double max = 0; 
     numbers = new ArrayList<Double>(NELEM); 
     //Finding maximum. 
     for(int i = 0; i < NELEM; i++){ 
      Double currElem = Math.random(); 
      if(currElem > max) max = currElem; 
      numbers.add(currElem); 
     } 
     //Mapping process 
     int offset = 0; 
     for(int j = 0; j < NELEM; j++){ 
      Integer mapped = (int) ((JFRAME_WIDTH * numbers.get(j))/max); 
      myRectangle rect = new myRectangle(offset , mapped); 
      drawables.add(rect); 
      offset += JFRAME_HEIGHT/NELEM; 
     } 
    } 

    private class myRectangle{ 

     int myy , wid , colorR,colorG,colorB; 

     public myRectangle(int y , int wid){ 
      this.myy = y; 
      this.wid = wid; 
      Random r = new Random(); 
      colorR = r.nextInt(255); 
      colorG = r.nextInt(255); 
      colorB = r.nextInt(255); 

     } 
    } 

    private class myPanel extends JPanel{ 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      for(myRectangle rectan : drawables){ 
       Graphics2D graphics2D = (Graphics2D)g; 
       System.out.println(rectan.wid); 
       Rectangle2D.Double rect = new Rectangle2D.Double(0,rectan.myy,rectan.wid,JFRAME_HEIGHT/NELEM); 
       graphics2D.setColor(new Color(rectan.colorR,rectan.colorG,rectan.colorB)); 
       graphics2D.fill(rect); 
      } 
      System.out.println("===================================================================================================="); 
     } 
    } 


} 
+0

lock.lock()'즉,'전화는 GUI가 할 수없는 원인 이벤트 발송 쓰레드를 차단 그 자체를 다시 칠하십시오. 하드와 함께 확실하게 [MCVE] (http://stackoverflow.com/help/mcve) 비록 – dic19

+0

정직하게 본질적인 내용을 제거하지 않고 코드를 짧게 만드는 방법을 모른다. 그게 전부입니다. 다른 파일은 없습니다. – arocketman

답변

3

대부분의 OS (또는 오히려 그들이 사용하는 UI 프레임 워크) 동시 액세스를 지원하지 않습니다. 간단히 말해, 동시에 두 개의 텍스트 문자열을 렌더링 할 수 없습니다.

그래서 스윙은 UI 스레드에서 모든 렌더링 작업을 실행합니다. UI 스레드 외부에서 렌더링 기능 (예 : paint())을 호출하면 모든 종류의 문제가 발생할 수 있습니다. 그래서 당신이 그것을 할 때, 스윙은 "나는 다시 칠해야"하고 돌아가는 것을 기억할 것입니다 (실제 작업을하는 대신에). 그렇게하면 Swing이 당신을 보호하지만 대부분의 사람들은 유용한 메시지로 오류를 얻는 것을 선호합니다.

타이머는 항상 타이머가 만료 될 때 실행되는 스레드가 있음을 의미합니다. 이것은 Swing의 UI 스레드가 아닙니다. 따라서 모든 작업은 EventQueue.invokeLater() 또는 그와 비슷한 것으로 포장해야합니다.

또 다른 일반적인 버그는 UI 스레드를 잡아 먹는 것입니다 (복잡한 계산을하기 때문에 렌더링이 발생하지 않습니다). 그것이 SwingWorker입니다. 다시 말하지만, SwingWorker의 대부분의 메서드에서 무언가를 렌더링하는 메서드 호출을 금지합니다 (-> use invokeLater()).

내 생각 엔 UI 스레드가 잠금을 기다리고 잠금이 잠기지 않았거나 자주 잠금 해제되지 않는다고 생각합니다. See this demo 스윙에서 간단한 애니메이션을 만드는 방법.

public class TimerBasedAnimation extends JPanel implements ActionListener { 
    public void paint(Graphics g) { 
     // setup 
     // do some first-run init stuff 
     // calculate the next frame 
     // render frame 
    } 

    public void actionPerformed(ActionEvent e) { 
    repaint(); 
    } 

    public static void main(String[] args) { 
    JFrame frame = new JFrame("TimerBasedAnimation"); 
    frame.add(new TimerBasedAnimation()); 
    ... 
    } 
} 

코드에서 알 수 있듯이 잠기지 않습니다. 대신, "렌더링하기"이벤트를 actionPerformed에서 Swing으로 보냅니다. 얼마 후, Swing은 paint()로 전화 할 것입니다. 이 일이 발생할 때 아무 말도하지 않고 (또는 스윙을 강요하거나 강요 할 방법이 없습니다).

그래서 좋은 애니메이션 코드는 현재 시간을 가져 와서 그 시간에 애니메이션 상태를 계산 한 다음 렌더링합니다. 따라서 M 초 동안 맹목적으로 N 단계를 거치지 않습니다. 대신 모든 프레임에 맞게 애니메이션을 조정하여 애니메이션이 실제로는 부드럽지 않게 만듭니다.

관련 : 비 제로 기회가

+0

감사합니다, 당신은 당신의 대답에 매우 철저했습니다. 코드로 돌아가서 잠금 장치를 제거하고 제공 한 예제를 적용 해 보겠습니다. 좋은 하루 되세요! – arocketman

+0

@Aaron Digulla 코드 행 (루프)에 의해 발생하는 disagee입니다.'Graphics2D graphics2D = (Graphics2D) g;', 논리는 1에 맞습니다. 그림 외부에서'fill array'를 호출 한 후, 3. 페인트 작업 초기화 -'super.paintComponent (g);', 4.'setBackground in paintComponen't, 5. 마지막으로'paintComponent'의 페인트 (준비된 Ojbects, 좌표 등의 배열 내부 루프) , – mKorbel

+0

여전히 JVM이 다시 칠하기를 결정할 수 있습니다 :-) – mKorbel

관련 문제