# L&F Exceptions



## filth (26. Jan 2011)

Hallo,

meine Anwendung produziert zufällige Exceptions, deren Herkunft mir nicht ganz klar ist. Ich verwende das Nimbus L&F, die Exceptions haben keinerlei Einfluß auf den Programmablauf und treten zufällig, nicht reproduzierbar aber stetig auf.
Hier sind einige Beispiele:



> Date: Wed Jan 26 17:28:56 CET 2011
> You crashed thread AWT-EventQueue-0
> Exception was: java.lang.ClassCastException: com.sun.java.swing.plaf.nimbus.DerivedColor$UIResource cannot be cast to com.sun.java.swing.Painter
> Trace: --> at com.sun.java.swing.plaf.nimbus.NimbusStyle.getBackgroundPainter(Unknown Source)--> at com.sun.java.swing.plaf.nimbus.SynthPainterImpl.paintBackground(Unknown Source)--> at com.sun.java.swing.plaf.nimbus.SynthPainterImpl.paintPanelBackground(Unknown Source)--> at javax.swing.plaf.synth.SynthPanelUI.update(Unknown Source)--> at javax.swing.JComponent.paintComponent(Unknown Source)--> at Image.ImageAreaComponent.paintComponent(ImageAreaComponent.java:248)--> at javax.swing.JComponent.paint(Unknown Source)--> at javax.swing.JComponent.paintToOffscreen(Unknown Source)--> at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)--> at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)--> at javax.swing.RepaintManager.paint(Unknown Source)--> at javax.swing.JComponent._paintImmediately(Unknown Source)--> at javax.swing.JComponent.paintImmediately(Unknown Source)--> at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)--> at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)--> at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)--> at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)--> at java.awt.event.InvocationEvent.dispatch(Unknown Source)--> at java.awt.EventQueue.dispatchEvent(Unknown Source)--> at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)--> at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)--> at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)--> at java.awt.EventDispatchThread.pumpEvents(Unknown Source)--> at java.awt.EventDispatchThread.pumpEvents(Unknown Source)--> at java.awt.EventDispatchThread.run(Unknown Source)



Oder:



> Date: Wed Jan 26 17:33:45 CET 2011
> You crashed thread AWT-EventQueue-0
> Exception was: java.lang.ClassCastException: java.lang.Boolean cannot be cast to com.sun.java.swing.Painter
> Trace: --> at com.sun.java.swing.plaf.nimbus.NimbusStyle.getBackgroundPainter(Unknown Source)--> at com.sun.java.swing.plaf.nimbus.SynthPainterImpl.paintBackground(Unknown Source)--> at com.sun.java.swing.plaf.nimbus.SynthPainterImpl.paintPanelBackground(Unknown Source)--> at javax.swing.plaf.synth.SynthPanelUI.update(Unknown Source)--> at javax.swing.JComponent.paintComponent(Unknown Source)--> at Image.ImageAreaComponent.paintComponent(ImageAreaComponent.java:248)--> at javax.swing.JComponent.paint(Unknown Source)--> at javax.swing.JComponent.paintToOffscreen(Unknown Source)--> at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)--> at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)--> at javax.swing.RepaintManager.paint(Unknown Source)--> at javax.swing.JComponent._paintImmediately(Unknown Source)--> at javax.swing.JComponent.paintImmediately(Unknown Source)--> at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)--> at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)--> at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)--> at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)--> at java.awt.event.InvocationEvent.dispatch(Unknown Source)--> at java.awt.EventQueue.dispatchEvent(Unknown Source)--> at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)--> at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)--> at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)--> at java.awt.EventDispatchThread.pumpEvents(Unknown Source)--> at java.awt.EventDispatchThread.pumpEvents(Unknown Source)--> at java.awt.EventDispatchThread.run(Unknown Source)



Oder



> Date: Wed Jan 26 17:35:23 CET 2011
> You crashed thread AWT-EventQueue-0
> Exception was: java.lang.ClassCastException: javax.swing.plaf.FontUIResource cannot be cast to com.sun.java.swing.Painter
> Trace: --> at com.sun.java.swing.plaf.nimbus.NimbusStyle.getBackgroundPainter(Unknown Source)--> at com.sun.java.swing.plaf.nimbus.SynthPainterImpl.paintBackground(Unknown Source)--> at com.sun.java.swing.plaf.nimbus.SynthPainterImpl.paintPanelBackground(Unknown Source)--> at javax.swing.plaf.synth.SynthPanelUI.update(Unknown Source)--> at javax.swing.JComponent.paintComponent(Unknown Source)--> at javax.swing.JComponent.paint(Unknown Source)--> at Image.ImgShowComponent.paint(ImgShowComponent.java:330)--> at javax.swing.JComponent.paintChildren(Unknown Source)--> at javax.swing.JComponent.paint(Unknown Source)--> at javax.swing.JComponent.paintToOffscreen(Unknown Source)--> at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)--> at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)--> at javax.swing.RepaintManager.paint(Unknown Source)--> at javax.swing.JComponent._paintImmediately(Unknown Source)--> at javax.swing.JComponent.paintImmediately(Unknown Source)--> at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)--> at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)--> at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)--> at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)--> at java.awt.event.InvocationEvent.dispatch(Unknown Source)--> at java.awt.EventQueue.dispatchEvent(Unknown Source)--> at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)--> at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)--> at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)--> at java.awt.EventDispatchThread.pumpEvents(Unknown Source)--> at java.awt.EventDispatchThread.pumpEvents(Unknown Source)--> at java.awt.EventDispatchThread.run(Unknown Source)



Hat jemand eine Idee woran es liegt? 
Danke!


----------



## Marco13 (26. Jan 2011)

Schwer zu sagen... Ein heißer Kandidat bei Stichworten wie "GUI/L&F", "nicht reproduzierbar" usw. wäre ein Threading-Problem. Bist du sicher, dass ALLE deine Components NUR im EDT erzeugt werden, und JEDE Aktion, die das Aussehen einer Component beeinflussen kann, NUR auf dem EDT gemacht wird?


----------



## filth (27. Jan 2011)

Hmm das könnte sein.

In meiner Anwendung können durch den User Bilder geladen werden, beim Laden werden die in der View dargestellt, an diesem Punkt gibt es auch diese Exceptions.

Das ganze funktioniert folgendermaßen:

In der View der Button:


```
private JButton getAddFilesButton() {
		if(addFilesButton == null) {
			addFilesButton = new JButton();
			addFilesButton.setText("Bilder auswählen");			
			addFilesButton.setSize(235, 32);
			addFilesButton.addMouseListener(new MouseAdapter() {
	    		public void mousePressed(MouseEvent evt) {
	    			addFilesButtonMouseClicked(evt);
	    		}				
	    	});							
		}
		return addFilesButton;
	}
```

Die addFilesButtonMouseClicked() zeigt einen FileChooser an und triggert letzendlich eine Methode im Controller:
myController.addFiles( myFiles ); 

Dort wird ein Thread gestartet, der die Bilder auf die View packt und einen Progressbar verändert:


```
....
....
...
new Thread() {
			 public void run()
			 {
				// we will need the progressbar... 
				JProgressBar myProgBar = myView.getMyProgressBar();
				 
				//create image objects out of selected files and put images on view
				for (int i = 0; i < myEvent.getEventPics().size(); i++)
				{			
					String currentFile = myEvent.getEventPics().get(i);
										
					ImgShowComponent myImg = null;					
					try {
						myImg = new ImgShowComponent(myEvent, myCon, currentFile, galleryPicIndicator);
					} catch (InvalidPictureException e) {
						continue;
					}					
					myView.getImagesPanel().addImage(myImg);
															
					// update status bar
					int perCent = (i * 100) / myEvent.getEventPics().size();						
					myProgBar.setVisible(true);
					myProgBar.setValue(perCent);
					myProgBar.setStringPainted(true);
					myProgBar.setString("Bilder werden geladen: " + perCent +"%");
										
				}
				myProgBar.setVisible(false);
				myCon.setOperating(false);
				myCon.getView().getCancelLabel().setVisible(false);
			  }
		}.start();
```

Ist hier evtl das Problem zu suchen? Ist meine Herangehensweise falsch?


----------



## Marco13 (27. Jan 2011)

Joa, dort werden halt Swing-Components von einem Thread verändert, der NICHT der Event-Dispatch-Thread ist. Je nachdem, was dort so lange dauert, dass eine Progressbar angezeigt werden soll, muss man das antsprechend anders strukturieren. Grundsätzlich kann man einzelne, kleine Aufgaben, die in einem bestimmten Thread gemacht werden, auf dem EDT ausführen lassen, indem man solche Sachen wie

```
void run() // Im fremden Thread
{
    ...
    machWasMitDemGUI();
}
```
ändert in

```
void run() // Im fremden Thread
{
    ...
    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()  
        {
            machWasMitDemGUI();
        }
    });
}
```

Speziell für langwierige Aufgaben, die die GUI über einen "Fortschritt" (oder über Zwischenergebnisse) benachrichtigen, gibt es aber den SwingWorker (Java Platform SE 6) . Auf How to Use Progress Bars (The Java™ Tutorials > Creating a GUI With JFC/Swing > Using Swing Components) gibt es auch extra einen Abschnitt dazu, wie man SwingWorker mit ProgressBars verdengelt. Das könnte für deinen Fall das geeignetste Mittel sein.


----------



## filth (27. Jan 2011)

Danke für die Erklärung.
Ich habe versucht meinen Code entsprechend anzupassen:

Die ButtonOnClick-Methode hat nun diesen Block


```
new Thread() {
			 public void run()
			 {
			    SwingUtilities.invokeLater(new Runnable()
			    {
			        public void run()  
			        {
			        	addPicturesToView(myEvent, myCon, oldImageAmount, tmpFiles);
			        }
			    });
			 }
		}.start();
```

Es funktioniert, aber jetzt wird die View nicht laufend aktualisiert (die Bilder werden in einer Schleife auf die View gelegt) sondern die Aktualisierung erfolgt erst wenn die "addPicturesToView" durchgelaufen ist. Was mache ich falsch?


----------



## Marco13 (27. Jan 2011)

Ja, jetzt wird ja ALLES auf dem EDT gemacht. Wenn das zeitaufwändige das eigentliche Laden ist: Wie werden denn die Bilder im Moment geladen, oder speziell: Was passiert im Konstruktor von ImgShowComponent so alles?


----------



## filth (27. Jan 2011)

Der Konstruktor bzw relevante Teil der Klasse sehen (vereinfacht) so aus:



```
public class ImgShowComponent extends JPanel
{

	public ImgShowComponent( Event myEvent, Controller myController, String sFile, boolean isStartPic ) throws InvalidPictureException
	{
		// load the file and create the thumb from it
		createThumb(loadOriginalFile());
	}



	/**
	 * Will create the thumb out of given Image
	 */
	private boolean createThumb(BufferedImage myImage)
	{		
		// make sure we got a valid image
		if(myImage == null) return false;
			
		 // Querformat
		if(myImage.getWidth(this) > myImage.getHeight(this))
		{
			myThumb = ImageProcessor.getScaledInstance2(myImage, 150, 100, true);
		}
		// Hochformat
		else
		{
			myThumb = ImageProcessor.getScaledInstance2(myImage, 100, 150, true);
		}		
		
		// reload controls for the new size
		initGuiObject();
		
		return true;
	}


	/**
	 * gui init
	 */
	private void initGuiObject()
	{
		// get the icon handlers from view (control elements)
		ImageIcon deleteIcon = myController.getView().getDeleteIcon();
		ImageIcon rotateLeftIcon = myController.getView().getRotateLeftIcon();
		ImageIcon rotateRightIcon = myController.getView().getRotateRightIcon();
		ImageIcon eventPic = myController.getView().getEventPicIcon();
				
		JLabel deleteLabel = new JLabel(deleteIcon);
		JLabel rotateLeftLabel = new JLabel(rotateLeftIcon);
		JLabel rotateRightLabel = new JLabel(rotateRightIcon);
		JLabel eventPicLabel = new JLabel(eventPic);
		
		// pack view
		this.setLayout(null);
		this.add(deleteLabel);
		this.add(rotateLeftLabel);
		this.add(rotateRightLabel);
		this.add(eventPicLabel);
		
		// place controls under thumb
		deleteLabel.setBounds(myThumb.getWidth(this) - 15, myThumb.getHeight( this ) + controllYOffset, 20, 20);
		
		eventPicLabel.setBounds(39, myThumb.getHeight( this ) + controllYOffset, 20, 20);
		rotateLeftLabel.setBounds(22, myThumb.getHeight( this ) + controllYOffset, 20, 20);
		rotateRightLabel.setBounds(5, myThumb.getHeight( this ) + controllYOffset, 20, 20);
		
		// mouse listeners follow now
		
		// SET as start-pic
		eventPicLabel.addMouseListener(new MouseAdapter() {
			....					
			}
		});		
		
		deleteLabel.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent evt) {
				myController.removePic(filePath);				
			}
		});		
		
		// rotate left label
		rotateLeftLabel.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent evt) {
				....
			}
		});
		
		rotateRightLabel.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent evt) {
				....
			}
		});
		
		// set some styles
		setBorder(BorderFactory.createCompoundBorder(new LineBorder(new java.awt.Color(21,60,70), 1, true),null));
						
		// refresh window sizes and scrolls
		myController.refreshView();		
	}
}
```


----------



## Marco13 (27. Jan 2011)

OK, man kann davon ausgehen, dass einerseits das Laden des Bildes in getOriginalImage() lange dauert, insbesondere aber auch das runterskalieren auf Thumbnailgöße. 

Falls möglich wäre der erste "Refactoring"-Schritt diese Aufgaben und die GUI-Veränderungen zu trennen. Das zeitaufwändige Laden und Sklaieren würde dann von einem Hintergrundthread (eben einem SwingWorker) gemacht werden. Die "Zwischenergebnise" wären dann die fertigen Thumbnails, als BufferedImage (oder ggf. eigene Klasse mit Zusatzinformationen wie z.B. dem Dateinamen, falls das gebraucht wird). Diese Zwischenergebnisse würden dann in der
protected void process(List<BufferedImage> chunks) 
Methode des SwingWorkers verwendet, um sie den zu erstellenden ImgShowComponents direkt im Konstruktor zu übergeben, und die erstellten ImgShowComponents zum GUI hinzuzufügen.


----------



## filth (27. Jan 2011)

Damit ich das richtig verstehe:

- das Laden der Bilder und das Skalieren in der ImgShowComponent - Klasse würde ich in den SwingUtilities - Thread auslagern
- diese Ergebnisse sammele ich in eine Liste: List<BufferedImage> chunks

Wo bzw an welcher Stelle müsste ich diese Liste verarbeiten und die Elemente der GUI hinzufügen?


----------



## Marco13 (27. Jan 2011)

filth hat gesagt.:


> Damit ich das richtig verstehe:
> 
> - das Laden der Bilder und das Skalieren in der ImgShowComponent - Klasse würde ich in den SwingUtilities - Thread auslagern



 Nein, gerade umgekehrt. Das eigentlich zeitaufwändige (laden und skalieren) wird von einem anderen Thread gemacht - damit das GUI während dieser Zeit nicht blockiert. Wenn dieser Fremde Thread aber Daten produziert, die irgendwie "ans GUI weitergreicht" werden müssen, kann man SwingUtilities verwenden. Mit den SwingUtilities kann man die Dinge, die mit dem GUI zu tun haben, im Event-Dispatch-Thread ausführen lassen (also dem einzigen Thread, der etwas am GUI ändern darf).

Aber damit das nicht falsch ankommt: Wenn man einen SwingWorker verwendet, braucht man die SwingUtilities i.a. nicht mehr. Der SwingWorker ist speziell für solche Aufgaben konstruiert, und bietet Mechanismen an, mit denen man Daten ans GUI weiterreichen kann, OHNE das "per Hand" mit den SwingUtilities machen zu müssen.



> - diese Ergebnisse sammele ich in eine Liste: List<BufferedImage> chunks
> 
> Wo bzw an welcher Stelle müsste ich diese Liste verarbeiten und die Elemente der GUI hinzufügen?



Jein. Der SwingWorker enthält einen eigenen Thread, der die aufwändigen Berechnungen macht. Dabei generiert er Daten (hier: die Thumbnails). Diese Daten übergibt man an die "publish"-Methode des SwingWorkers. In dieser Methode werden die Daten erstmal in einer Liste abgelegt - quasi zwischengepuffert. In regelmäßigen Abständen sorgt der SwingWorker dann dafür, dass seine Methode
protected void process(List<BufferedImage> chunks) 
aufgerufen wird. Diese Methode bekommt eben die zwischengespeicherten Daten übergeben. Diese Methode wird aber im Event-Dispatch-Thread ausgeführt. Das heißt: Wenn man diese Methode überschreibt, bekommt man die Daten, die der Worker produziert hat. Und weil sie auf dem Event-Dispatch-Thread ausgeführt wird, kann man DORT die Daten verwenden, um GUI-Elemente daraus zu erstellen oder zu verändern.


----------



## filth (27. Jan 2011)

Ok danke - ich lese mich mal ein.


----------

