2012-10-10 3 views
6

나는 항복한다. 차단중인 항목을 찾으려면 몇 주가 지난 후 일련 번호 데이터가 내 코드의 그래픽 부분에서 업데이트되지 않도록하십시오. 처음 자바 프로그래밍. 약 15 년의 프로그래밍 경험을 가진 마이크로 및 내 자신의 문제를 해결하는 데 익숙해졌지만 여기에는 전술이 생산적 인 지점을 넘어갑니다. 내 응용 프로그램은 두 개의 파일로 구성됩니다.그래픽 업데이트

하나의 파일은 RXTX 프로젝트에서 유래하여 여러 패킷으로 전송 된 일련의 데이터를 매초 두 번씩 두 번씩 수신합니다. 이것은 매력 (시간이 걸렸습니다)처럼 작동하고 캡처 된 데이터가 정확하고 안정적임을 나타냅니다. 안정적입니다.

다른 파일은 그래픽이며 최종 사용자가 을 읽고 때때로 값을 쓸 수있는 약 80 개의 메뉴로 구성됩니다. 탐색은 지금까지 버튼과 스크롤 막대의 마우스 이벤트로 수행됩니다. 이 부분도 제대로 작동합니다. 값을 읽고, 변경하고 저장할 수 있습니다.

내가 붙어있는 부분은 업데이트 된 값이 직렬 파일에서 업데이트되지 않으므로 그래픽 화면입니다. 행운을 빌어 수백 가지 예제와 튜토리얼 (이 사이트에서 많은 )을 따라 가려고 시도했습니다.

객체 관련 언어의 개념은 나에게 새롭고 여전히 혼란 스럽습니다. 내 문제가 상속과 수업에 관련되는지 확실히 확인하십시오. 스레드는 다른 후보입니다 ... 을 실행하고 내 문제를 제시하고 다른 사람이 무엇이 잘못 볼 수 있기를 기대하는 가장 작은 크기로 코드를 줄이십시오.

package components; 

import gnu.io.CommPort; 
import gnu.io.CommPortIdentifier; 
import gnu.io.SerialPort; 
import gnu.io.SerialPortEvent; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import javax.swing.SwingUtilities; 

public class SerialComm extends ScreenBuilder implements java.util.EventListener { 

InputStream in; 

public SerialComm() { 
    super(); 
} 

public interface SerialPortEventListener 
     extends java.util.EventListener { 
} 

void connect(String portName) throws Exception { 
    CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier("COM1"); 
    if (portIdentifier.isCurrentlyOwned()) { 
     System.out.println("Error: Port is currently in use"); 
    } else { 
     CommPortIdentifier.getPortIdentifier("COM1"); 
     System.out.println("" + portName); 
     CommPort commPort = portIdentifier.open("COM1", 2000); 
     if (commPort instanceof SerialPort) { 
      SerialPort serialPort = (SerialPort) commPort; 
      serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_2, SerialPort.PARITY_NONE); 
      InputStream in = serialPort.getInputStream(); 
      OutputStream out = serialPort.getOutputStream(); 
      serialPort.addEventListener(new SerialComm.SerialReader(in)); 
      serialPort.notifyOnDataAvailable(true); 

      (new Thread(new SerialComm.SerialReader(in))).start(); 
      // TX functionality commented for now 
      //    (new Thread(new SerialWriter(out))).start(); 

     } else { 
      System.out.println("Error: Only serial ports are handled by this  example."); 
     } 
    } 
} 

public class SerialReader extends SerialComm implements Runnable, 
     gnu.io.SerialPortEventListener { 

    public SerialReader(InputStream in) { 
     this.in = in; 
    } 

    @Override 
    public void run() { 
    count=11; // just for test. run is normally empty 
    count2=count; // and real code runs within serialEvent() 
    System.out.println("SerialReader " + count); 
    dspUpdate(); // do some desperate stuff in graphics file 
    System.out.println("Post Update " + count); 
    } 

    @Override 
    public void serialEvent(SerialPortEvent event) { 
    System.out.println("SerialEvent"); 
     switch (event.getEventType()) { 
      case SerialPortEvent.DATA_AVAILABLE: 
       try { 
        synchronized (in) { 
         while (in.available() < 0) { 
          in.wait(1, 800000); 
         } //in real code RX data is captured here twice a sec 
        } //and stored into buffers defined in ScreenBuilder 
    //dspUpdate() is called from here to make ScreenBuilder update its screen 
    //That never happens despite all my attempts    
       } catch (IOException e) { 
        System.out.println("IO Exception"); 
       } catch (InterruptedException e) { 
        System.out.println("InterruptedException caught"); 
       } 
     } 
    } 
} 

/* "main" connect PC serial port and start graphic part of application 
* To demonstrate problem with no serial data stream present 
* order of init between serial port and graphics are switched 
*/ 

public static void main(String[] args) { 

    SwingUtilities.invokeLater(new Runnable() { 

     @Override 
     public void run() { 
      ScreenBuilder screen = new ScreenBuilder(); 
      screen.createAndShowGUI(); 
      System.out.println("Created GUI"); 
     } 
    }); 
    try { 
     (new SerialComm()).connect("COM1"); 
    } catch (Exception e) { 
     System.out.println("Error"); 
     e.printStackTrace(); 
    } 
    } 
} 

그리고 그래픽

package components; 

import java.awt.*; 
import javax.swing.SwingUtilities; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.BorderFactory; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.event.*; 

public class ScreenBuilder extends JPanel implements ActionListener { 

public Font smallFont = new Font("Dialog", Font.PLAIN, 12); 
Color screenColor; 
Color lineColor; 
short btn=0; 
short count; 
short count2; 
Button helpButton; 

public static void createAndShowGUI() { 
    System.out.println("Created GUI on EDT? " 
      + SwingUtilities.isEventDispatchThread()); 
    JFrame f = new JFrame("JUST A TEST"); 
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    f.add(new ScreenBuilder()); 
    f.pack(); 
    f.setVisible(true); 
} 

public void dspButton() { 
    setLayout(null);// 
    helpButton = new Button("?"); 
    helpButton.setLocation(217, 8); // set X, Y 
    helpButton.setSize(16, 14); //Set Size X, Y // 
    helpButton.addActionListener(this); 
    add(helpButton); 
    setBackground(Color.black); 
    helpButton.setBackground(Color.black); 
    screenColor = Color.black; 
    helpButton.setForeground(Color.white); 
    lineColor = Color.white; 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    if (e.getSource() == helpButton) { 
     count2++; 
     System.out.println("Pressed Button "); 
     repaint(); 
    } 
} 

public ScreenBuilder() { 
    setBorder(BorderFactory.createLineBorder(Color.black)); 
} 

@Override 
public Dimension getPreferredSize() { 
    return new Dimension(240, 180); 
} 

public void dspUpdate() { 
    /* 
    * This function is called from SerialComm 
    * Should be called when serial packets have arrived (twice a second) 
    * and update screen with values from serial stream 
    * For now just a test var to validate that values from SerialComm 
    * get to here (they do) 
    */ 
count++; 
System.out.println("Update Count " + count); 
System.out.println("Update Count2 " + count2); 
// revalidate(); // another futile attempt to update screen 
// repaint(); 
} 

@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    g.setColor(lineColor); 
    g.setFont(smallFont); 
    count++; 
    g.drawString("" + count, 130, 20); 
    g.drawString("" + count2, 150, 20); 
    if (btn == 0) { 
     dspButton(); 
     btn = 1; 
    } 
    } 
} 
+4

절대로 절대 양보하지 말고 – mKorbel

+0

나는 스윙에 익숙하지 않지만 메서드 호출 간의 관계를 설명 할 수 있습니까? 내가 얻지 못하는 것을 설명하는 것은 약간 까다 롭다. 첫째로'SerialComm'은'dspUpdate()'를 호출한다. 이 메소드는'repaint'를 호출 할 것이고 (좋은 것 같아요), 다시 칠하는 것은'dspUpdate'를 호출하는'paintComponent'를 다시 호출할까요? – phineas

+0

@phineas 스윙의 Concurency 문제가 있습니다. GUI에 대한 모든 업데이트는 EDT – mKorbel

답변

2

당신이 GUI 클래스로 최선을 다하는됩니다로 실행하고있는 가장 큰 문제를 제기. 모델 (백엔드 직렬 통신 내용)을 프론트 엔드 (예쁜 GUI 구성 요소)에서 분리하려고하면 많은 두통을 피할 수 있습니다. 이 예제에서는 하나의 파일에 있지만 모델, 뷰 및 컨트롤 (모델과 뷰간에 통신하는 내용)을 3으로 구분해야합니다.

샘플 통신 대신 모델에 직렬 통신 코드를 추가하면 너무 많은 번거 로움없이 뷰와 모델간에 통신 할 수 있어야합니다. 최대한 많은 코드를 보존하려고했습니다.

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 

public class TranslucentWindow { 

    public static void main(String[] args) { 

     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        View screen = new View(); 
        System.out.println("Created GUI"); 
        Model model = new Model(); 

        Control c = new Control(screen, model); 
       } catch (Exception e) { 
        System.out.println("Error"); 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    //Only cares about the backend. Simplified because you said all the backend code was working right. 
    public static class Model{ 

     //Data that was updated - you can change this to whatever you want. 
     public String count; 
     //Listener that notifies anyone interested that data changed 
     public ActionListener refreshListener; 

     public void run() { 
      //As a sample, we're updating info every 1/2 sec. But you'd have your Serial Listener stuff here 
      Thread t = new Thread(new Runnable(){ 
       @Override 
       public void run() { 
        int i = 0; 
        while(true){ 
         dspUpdate(i++); 
         try { 
          Thread.sleep(500); 
         } catch (InterruptedException e) { 
          e.printStackTrace(); 
         } 
        } 
       }}); 
      t.start(); 
     } 

     //Update data and notify your listeners 
     public void dspUpdate(int input) { 
      count = String.valueOf(input); 
      System.out.println("Update Count " + count); 
      refreshListener.actionPerformed(new ActionEvent(this, input, "Update")); 
     } 

    } 


    //Only cares about the display of the screen 
    public static class View extends JPanel { 

     public Font smallFont = new Font("Dialog", Font.PLAIN, 12); 
     Color screenColor; 
     Color lineColor; 
     short btn=0; 
     String modelRefreshInfo; 
     int buttonPressCount; 
     Button helpButton; 

     public View(){ 
      //Build Panel 
      dspButton(); 

      //Create and show window 
      System.out.println("Created GUI on EDT? "+ SwingUtilities.isEventDispatchThread()); 
      JFrame f = new JFrame("JUST A TEST"); 
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      f.add(this); 
      f.pack(); 
      f.setVisible(true); 
     } 

     public void dspButton() { 
      setLayout(null);// 
      helpButton = new Button("?"); 
      helpButton.setLocation(217, 8); // set X, Y 
      helpButton.setSize(16, 14); //Set Size X, Y // 
      add(helpButton); 
      setBackground(Color.black); 
      helpButton.setBackground(Color.black); 
      screenColor = Color.black; 
      helpButton.setForeground(Color.white); 
      lineColor = Color.white; 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(240, 180); 
     } 

     @Override 
     public void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      g.setColor(lineColor); 
      g.setFont(smallFont); 
      g.drawString("ModelUpdates: " + modelRefreshInfo, 10, 20); 
      g.drawString("RefreshCount: " + buttonPressCount, 10, 40); 
      if (btn == 0) { 
       dspButton(); 
       btn = 1; 
      } 
     } 
    } 

    //Links up the view and the model 
    public static class Control{ 
     View screen; 
     Model model; 

     public Control(View screen, Model model){ 
      this.screen = screen; 
      //Tells the screen what to do when the button is pressed 
      this.screen.helpButton.addActionListener(new ActionListener(){ 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        //Update the screen with the model's info 
        Control.this.screen.buttonPressCount++; 
        System.out.println("Pressed Button "); 
        Control.this.screen.repaint(); 
       } 
      }); 

      this.model = model; 
      //Hands new data in the model to the screen 
      this.model.refreshListener = new ActionListener(){ 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        //Update the screen with the model's info 
        Control.this.screen.modelRefreshInfo = Control.this.model.count; 
        System.out.println("Model Refreshed"); 
        Control.this.screen.repaint(); 
       } 
      }; 

      //Starts up the model 
      this.model.run(); 
     }  
    } 
} 
+0

+1'SwingWorker'를 사용 하겠지만 직렬 IO를위한 별도의 스레드는 필수적입니다. 이 [답변] (http://stackoverflow.com/a/12731752/230513)도 참조하십시오. – trashgod

+0

@Nick Rippe 감사합니다. "실제"직렬 코드는 그래픽 부분이 공유하는 버퍼에 ~ 200 바이트를 저장하는 것과 관련이 있습니다. 이것은 500ms마다 발생하므로 이벤트는 내 "타이머"가됩니다. 메뉴를 스크롤 할 때 나중에 표시 될 수있는 버퍼의 "숨겨진"값을 새로 고쳐야합니다. 이 코드가 제공 한 코드에서 발생합니까? 내 원래 코드는 9000 줄 이었으므로 제안 사항에 따라 다시 넣으려면 시간이 걸립니다. 길을 따라 몇 가지 질문을하면서 내일 다시 오게 될 것입니다. 지금 자러 갈 필요가 있습니다 ... /Richard – user1735586

+0

같은 다른 값을 동일한 수신기에 밀어 넣을 수 있어야합니다. 모델보기에서 정보를 전달하는 이와 같은 라인을 추가하십시오. 'Control.this.screen.modelRefreshInfo = Control.this.model.count; ' –