2016-04-16 11 views
0

Ich bin ein Neuling in Swing und AWT. Ich habe Huffman Tree in GUI implementiert. Aber in dem Code, den ich geschrieben habe, ist die Ausgabe in Ubuntu (JAVA 8) sehr seltsam. Manchmal druckt es den Baum (manchmal willkürlich), manchmal bleibt das Fenster leer. Die Funktion paintComponent wird zu unterschiedlichen Zeiten für unterschiedliche Runs aufgerufen. Der Code ist der gleiche, keine Benutzereingabe, aber für einen anderen Lauf ist das Ergebnis anders. Bitte führen Sie den Code mindestens 5 Mal, um zu sehen, was ich gesagt habe. Kann mir jemand helfen, aus diesem Problem herauszukommen?
N.B. Sie können direkt zur Klasse Huffman springen. Unterschiedliche Ausgabe für genau den gleichen Code mit Java Swing und AWT

import javax.swing.JFrame; 
import javax.swing.JPanel; 
import java.awt.Graphics; 
import java.io.*; 
import java.util.*; 

import java.util.LinkedList; 
import java.util.PriorityQueue; 
import java.util.Queue; 

class Node implements Comparable<Node> { 
    private char ch; 
    private int freq; 
    private Node left, right; 
    private int x,y ; 

    Node(char ch, int freq, Node l, Node r) { 
     this.ch = ch; 
     this.freq = freq; 
     left = l; 
     right = r; 
    } 

    public boolean isLeaf(){ 
     return (left == null) && (right == null); 
    } 

    public int compareTo(Node that) { 
     return Integer.compare(this.freq,that.freq); 
    } 

    public Node getLeft(){ 
     return left; 
    } 

    public Node getRight(){ 
     return right; 
    } 

    public int getFreq(){ 
     return freq; 
    } 

    public char getCharacter(){ 
     return ch; 
    } 

    public void setPosition(int ht, int wd){ 
     x=wd; y=ht; 
    } 

    public int getX(){ 
     return x; 
    } 

    public int getY(){ 
     return y; 
    } 

    public String getString(){ 
     if(isLeaf()) return new String((Integer.toString(freq) + ' ' + ch)); 
     else return new String(Integer.toString(freq)); 
    } 
} 

public class Huffman extends JPanel{ 
    int height=0; 
    String str; 
    int[] freq = new int[256]; 
    Node root; 
    Queue<Node> q = new LinkedList<Node>(); 

    static final private int LEFT = 5; 
    static final private int RIGHT = 1300; 
    static final private int TOP = 5; 
    static final private int BOTTOM = 700; 
    static final private int RADIUS = 15; 

    public Huffman(){ 
     getStr(); 
     setFrequency(); 
     buildTree(); 
    } 

    public void getStr(){ 
     //Scanner sc = new Scanner(System.in); 
     //str = sc.nextLine(); 
     str = new String("What is happening?"); 
    } 

    public void setFrequency(){ 
     for(int i=0;i<str.length();i++) freq[str.charAt(i)]++; 
    } 

    public void buildTree(){ 
     PriorityQueue<Node> PQueue = new PriorityQueue<Node>(); 
     for(int i=0;i<256;i++){ 
      if(freq[i]!=0){ 
       PQueue.add(new Node((char)i, freq[i], null, null)); 
      } 
     } 

     while(PQueue.size()>1){ 
      Node left, right; 
      left = PQueue.poll(); 
      right = PQueue.poll(); 
      PQueue.add(new Node('\0',left.getFreq()+right.getFreq(),left,right)); 
      q.add(left); 
      q.add(right); 
     } 
     root = PQueue.poll(); 
     q.add(root); 
     setCoOrdinates(root,1,1); 
    } 

    public void setCoOrdinates(Node node, int wd, int ht){ 
     if(node == null) return; 
     height = Math.max(height,ht); 
     node.setPosition(wd,ht); 
     setCoOrdinates(node.getLeft(),2*wd-1,ht+1); 
     setCoOrdinates(node.getRight(),2*wd,ht+1); 
    } 

    public int getGraphicsX(int x, int y){ 
     return ((RIGHT-LEFT)/((int)Math.pow(2,y-1)+1))*x + LEFT; 
    } 

    public int getGraphicsY(int x, int y){ 
     return ((BOTTOM-TOP)/(height+1))*y + TOP; 
    } 

    @Override 
    public void paintComponent(Graphics g){ 

     //this '*' is printing for multiple times 
     System.out.println("*"); 

     while(q.isEmpty()==false){ 

      Node node = q.poll(); 

      int x = getGraphicsX(node.getX(),node.getY()), y = getGraphicsY(node.getX(),node.getY()); 

      String str = node.getString(); 

      g.drawOval(x-RADIUS,y-RADIUS,2*RADIUS,2*RADIUS); 
      g.drawString(str,x,y+(RADIUS/2)); 

      if(node.isLeaf()==false){ 
       int leftX,leftY,rightX,rightY; 

       leftX = getGraphicsX(node.getLeft().getX(), node.getLeft().getY()); 
       leftY = getGraphicsY(node.getLeft().getX(), node.getLeft().getY()); 

       rightX = getGraphicsX(node.getRight().getX(), node.getRight().getY()); 
       rightY = getGraphicsY(node.getRight().getX(), node.getRight().getY()); 

       g.drawLine(x, y+RADIUS, leftX, leftY-RADIUS); 
       g.drawLine(x, y+RADIUS, rightX, rightY-RADIUS); 

      } 
     } 
    } 

    public static void main(String[] args) { 

     JFrame jFrame = new JFrame(); 
     jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     jFrame.setSize(RIGHT,BOTTOM); 
     Huffman h = new Huffman(); 

     jFrame.add(h); 
     jFrame.setVisible(true); 

    } 
} 

Antwort

5
@Override 
public void paintComponent(Graphics g){ 
    ... 
    while(q.isEmpty()==false){ 

     Node node = q.poll(); 

Das Problem ist, dass Sie die Warteschlange innerhalb paintComponent sind zu ändern. Das UI-System kann zu jeder Zeit paintComponent aufrufen. Wenn Sie z. B. ein anderes Fenster über das Bedienfeld ziehen, werden die Repaints erneut angezeigt.

paintComponent sollte daher staatenlos sein und nicht die Warteschlange ändern.

Wenn Ihre Verwendung von poll unbedingt notwendig ist, eine einfache Lösung ist, um die Warteschlange zu kopieren:

@Override 
public void paintComponent(Graphics g){ 
    Queue<Node> q = new LinkedList<>(this.q); 

Scheint, wie Sie auch mit einer for-Schleife jede nur laufen könnte.


paar andere Dinge:

  • Ihr Code in main, wo Sie erstellen die GUI in einem Aufruf an SwingUtilities.invokeLater gewickelt werden muss:

    public static void main(String[] args) { 
        SwingUtilities.invokeLater(new Runnable() { 
         @Override 
         public void run() { 
          // create the GUI here 
         } 
        }); 
    } 
    

    Dies liegt daran, Swing ist Single mit Gewinde und nicht threadsicher. Siehe Initial Threads.

  • Sie super.paintComponent nennen sollen, die die JPanel (Hintergrundfarbe, etc.) malt:

    @Override 
    protected void paintComponent(Graphics g) { 
        super.paintComponent(g); 
    
  • Wie im vorherigen Codeausschnitt gezeigt, paintComponent ist eine protected Methode und es gibt keinen Grund, es zu machen public.

  • Sie sollten setSize nicht auf einem JFrame verwenden. Wenn der Rahmen eine feste Größe haben soll, sollten Sie getPreferredSize() auf dem JPanel überschreiben, dann rufen Sie pack() auf dem Rahmen. Der Rahmen wird automatisch um das Panel in der Größe herum angeordnet. (Beispiel gezeigt here.) Die Größe eines JFrame umfasst z.B. title bar und borders so mit setSize interferiert wahrscheinlich auch Ihre Malkoordinaten.