Swing JPanel wird überschrieben

not_me

Mitglied
Hallo, ich habe ein Programm, das Daten von einem Server herunter läd und verarbeitet. Diese beiden Vorgänge erzeugen Logeinträge, die verschiedenfarbig in einem Fenster angezeigt werden. Da der gesammte Prozess biszu 10min dauern kann, wird eine Progressbar angezeigt, damit der User weiß, dass das Programm noch läuft. Es gibt jeweils eine Klasse für den Download, das LogWindow und die Progressbar. Jede Klasse wird vom Main Programm als eingenständiger Thread gestartet. Das klappt auch alles sehr gut, jedoch wird am Ende der Inhalt des LogWindows fast immer gelöscht bzw. übermahlt. Ich komme einfach nicht dahinter, wiso das so ist. Ich hoffe jemand kann mir helfen. Zu Testzwecken habe ich das Programm stark verkürzt und den Download-Teil durch einen einfachen Counter ersetzt. Hier der Code:
Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;

import java.math.BigInteger;
import info.clearthought.layout.TableLayout;


// Calculate the power of 0 too 1000 and show the result of every step in a log window.
// While the calculation is running, a progress bar is shown in an additional window.
public class LogWindowTest
{ 	
	private static myProgressBar tProgress;					// A progress bar
	private static counter tCounter;						// A counter, to waste some time
	private static LogWindow lw;							// Shows the log in a window
	
	// For alternately changing the color of a log entry
	private static boolean lastEntryWasBlack = false;	
	
	
	public static void main( String args[] ) throws Exception
	{
		newRun();  				// Do a run on program start  
	}
	
	// Create and start the threads 
	public static void newRun()
	{
		// Create new progress bar
		tProgress = new myProgressBar(); 
		
		// Create new counter thread
		tCounter =   new counter();		
		tCounter.setMax(1000);
		
		// Create and start  new log window
		lw = new LogWindow();
		lw.setData();	    
		lw.start();
		lw.showWindow();
		
		// Start the progress bar and the counter thread
		tProgress.start();
		tCounter.start(); 		
		
		System.out.println("New run started...");
	}
	  
	// Stop the counter thread
	public static void stopCounter()
	{
		tCounter.brake();
	}
	
	public static void stopProgressBar()
	{
		tProgress.close();
	}
	  
	// Refresh the log window
	public static void refreshWin()
	{
		lw.refreshWindow();
	}
	
	
	// Reduce BigInteger b to a maximum of 10 digits
	// & add it to the log
	public static void addToLog(BigInteger b, int x)
	{
		String bAsString=""; 
		
		// Reduce BigInteger b to a maximum of 10 digits
		if (b.bitLength() > 10 )
		{			
			byte[] bArray = b.toByteArray();
			int max = bArray.length;
			
			if (max > 10){
				max = 10;
			}
			
			for (int i=0; i<max; i++)
			{
				bAsString = bAsString + bArray[i];
			}
			bAsString = bAsString + "...";			
		}
		else{
			bAsString = b.toString();
		}
		
		// Create output text
		String text = ( x + "^" + x + " = " + bAsString );
		
		// Add text to log, alternately in black or blue
		if (lastEntryWasBlack){
			lw.newLogListEntry(text, Color.BLUE);
			lastEntryWasBlack = false;
		}
		else{
			lw.newLogListEntry(text, Color.BLACK);
			lastEntryWasBlack = true;
		}
		
	}	  
}

// Creates a window (JFrame) and add a progress bar to it.
class myProgressBar extends Thread implements ActionListener 
{	
	private JProgressBar progressBar;
	private JLabel label = new JLabel ("Download process:");
	private JButton btnCancel = new JButton("Cancel");
	private JFrame frame = new JFrame();
	
	// The main method, that creates the frame for the progress bar
	public void run()
	{		
		// set title and add action listener for the button
		frame.setTitle("Loading System Dump. Please wait...");
		this.btnCancel.addActionListener(this);
		
		// Set the TableLayout for the frame	    
	    double size[][] =
	    {{ 0.04, TableLayout.FILL, TableLayout.FILL, TableLayout.FILL, 0.04,},				 // 5 columns				
	    { 0.04, TableLayout.FILL, 0.04, TableLayout.FILL, 0.04, TableLayout.FILL, 0.04 } };	 // 7 rows
	    frame.setLayout (new TableLayout(size));
	    
	    // set icon and screen size
		frame.setSize(350, 100);
		
		// Create the progress bar
		this.progressBar = new JProgressBar();
		// the progress bar is always running from left to right and back
		this.progressBar.setIndeterminate(true);		
		this.progressBar.setSize(100, 50);
		
		// add all elements to the frame
		frame.add(label, "1, 1, 3, 1");
		frame.add(progressBar, "1, 3, 3, 1" );		
		frame.add(btnCancel, "2, 5, 1, 1");
	     
		// set screen position and show the frame
		frame.setLocation(150, 150);
		frame.setVisible(true);
	}
	
	// Close the update progress bar
	public void close() 
	{	
		System.out.println("... run completed");
		frame.dispose();
	}	
	
	// If button 'cancel' gets pushed: call 'close()'
	public void actionPerformed(ActionEvent arg0) 
	{
		close();
		LogWindowTest.stopCounter();
	}
}
	  
// The counter thread, that waist the time and create the output  
class counter extends Thread   
{
	private myProgressBar p = null;
	private int max = 0;
	private boolean boBrake = false;	
	
	// Set the maximum 
	public void setMax(int newMax){
		max = newMax;		
	}
	
	// Calculation
    public void run()  
    {
    	int i=0;
    	
    	while ( i<max && !boBrake ){	    		
    		BigInteger a = BigInteger.valueOf(i).pow(i);
    		LogWindowTest.addToLog(a, i);							// add to log
    		i++;
    	} 	
    	
    	LogWindowTest.refreshWin();
    }
    
    // Stop the calculation (caused by the cancel button of the progress bar)
    public void brake()
    {
    	boBrake = true;
    }
    
    // Stop/close the progress bar
    public void stopProgBar()
    {
    	LogWindowTest.stopProgressBar();
    }
}

// The window, that show the update progress
class LogWindow extends Thread
{	 
	private JList logList;
	private JMenuItem jmiEnde, jmiNewRun;
	private Listener al;
	private Container lfwContainer;				
	private JPanel panlogListe;	
	private JFrame fLog;									
	private DefaultListModel model;				
	private LogListEntry entry;
	
	// Prepare the needed elements
	public void setData()
	{
		logList = new JList();
		al = new Listener();
		fLog = new JFrame();									
		model = new DefaultListModel();				
	}
	
	// create a new window on start()
	public void run(){	
		createWindow();
	}
	
	// Create a new window
	public void createWindow(){		
		
		// Create a new JList & add 'model'
		logList.setModel(model);								// 'model' contains the log data
		logList.setCellRenderer( new LogListCellRenderer() );
		
		lfwContainer = fLog.getContentPane();	 				// get the container of fLog   
		lfwContainer.add(logList);
	    	    

		// Create new JPanel AND add the List
		panlogListe = new JPanel( new BorderLayout() );
		panlogListe.add(new JScrollPane(logList), BorderLayout.CENTER);
		
		// Creates MenuBar, Menu AND Menu-Items
		JMenuBar jmb = new JMenuBar();
		JMenu jmFile = new JMenu("File");
		jmiEnde = new JMenuItem("Close");
		jmiNewRun = new JMenuItem("New run");
		
		// Add action-listener to Menu-Items
		jmiEnde.addActionListener(al);
		jmiNewRun.addActionListener(al);
		
		// Add every thing to MenuBar
		jmFile.add(jmiNewRun);
		jmFile.addSeparator();
		jmFile.add(jmiEnde);
		jmb.add(jmFile);
		
		// Add every thing to window and set the properties
		fLog.setJMenuBar(jmb);
		fLog.setTitle("Update Status");
		fLog.add(panlogListe);
		fLog.setLocation(520, 120);
		fLog.setSize(500, 600);
	}
	
	// Show the window (visibility is set to true)
	public void showWindow()
	{		
		fLog.setVisible(true);
	}
		
	// Close the window
	public void closeWindow()
	{		
		fLog.dispose();
	}
	
	// Refresh the window
	public void refreshWindow()
	{		
		fLog.repaint();
		fLog.validate();
	}
	
	// Creates a new log entry, that will have the content 'text' 
	// and will be shown in color 'col' 
	public void newLogListEntry(String text, Color col)
	{		
		entry = new LogListEntry(text, col);
		model.addElement(entry);
	}
	
	// ActionListener for Menu-Items 'New run' & 'End' 
	private class Listener implements ActionListener
	{
		public void actionPerformed(ActionEvent e)
		{
			if (e.getSource() == jmiNewRun)	
			{
				fLog.dispose();					// Close the log window
				createWindow();					// Create a new one
				LogWindowTest.newRun();			// Start a new run
			}
			
			if (e.getSource() == jmiEnde)	// Exit the program
			{
				fLog.dispose();
			}
			
		}
	}
		
	// Definition of object: 'LogListEntry'
	// to create use: LogListEntry(String content, Color fontcolor)
	public class LogListEntry {
	
		  private String content;
		  private Color fontcolor;	
		  
		  public LogListEntry(String content, Color fontcolor){
		     this.content = content;
		     this.fontcolor = fontcolor;
		  }
		  
		  public String getContent() 
		  {
		    return content;
		  }
		  public void setContent(String content)
		  { 
		    this.content = content;
		  }
		  public Color getFontcolor() 
		  {
		    return fontcolor;
		  }
		  public void setFontcolor(Color fontcolor) 
		  {
		    this.fontcolor = fontcolor;
		  }
	}
	
	// Change some features of ListCellRenderer for a better color-handling in the log-output
	public class LogListCellRenderer extends JLabel implements ListCellRenderer {
		  
		private static final long serialVersionUID = 1977L;

		@Override
		  public Component getListCellRendererComponent(JList list, Object value,
		      int index, boolean isSelected, boolean cellHasFocus) {

		    // Cast a LogListValue as LogFileWindow.LogListEntry 
			LogListEntry entry = (LogListEntry) value;

		    // set text to entry-content
		    this.setText(entry.getContent());
		    
		    // Activate 'this.setBackground'
		    this.setOpaque(true);
		    
		    // If List-Item is Selected:
		    if(isSelected){
		    	// Font-Color
		    	this.setForeground(Color.MAGENTA);
		    	// Background-Color
		    	this.setBackground(Color.LIGHT_GRAY);
		    }
		    // If List-Item is NOT Selected:
		    else{
		      // Font-Color
		      this.setForeground(entry.getFontcolor());
		      // Background-Color
		      this.setBackground(UIManager.getColor("logList.background"));
		    }		    
		    // Return this Label
		    return this;
		  }
	}
}
 
die meisten GUI-Elemente dürfen nicht wild von anderen Threads verändert werden,
wenn das mit Zeichen-Vorgängen kollidiert kann allles mögliche schiefgehen,
hier eine sichere Variante für deinen counter:

Java:
		while (i < max && !boBrake) {
			final BigInteger a = BigInteger.valueOf(i).pow(i);
			final int fi = i;
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					LogWindowTest.addToLog(a, fi); // add to log
				}
			});
			i++;
		}

wenn die Schleife schnell ist, so wie hier, dann kann auch der gesamte Block komplett in ein invokeLater() gesetzt werden,
wenn aber das ganze 10 min dauert und nur alle paar sec in praktisch nicht messbarer Zeit ein Eintrag erzeugt wird, dann nur diesen einzelnen Aufruf mit invokeLater(),
denn solange ein invokeLater() von der GUI ausgeführt wird, ist die GUI blockiert, keine Reaktion, kein Neuzeichnen
 
Zuletzt bearbeitet von einem Moderator:
Also im Testprogramm läufts super, danke. Mal nachher in das Hauptprogramm über tragen. *edit* Da läufts auch super, vielen Dank.
 
Zuletzt bearbeitet:

Zurück
Oben