JBusyComponente

ballibum

Aktives Mitglied
Hallo Forum,
ich habe mal wieder ein Problem, ich kriege meine JBusyComponente nicht zum Laufen. Ich habe eine JPane mit einem Button, welcher eine Funktion startet, dieses Funktioniert auch wie es soll. Nur leider erscheint über dem Button keine Art Eieruhr, wodurch signalisiert wird, dass das Programm noch arbeitet.
Hier ist der Code der JPane:
Java:
public class SimSeite {
	
	JPanel simPanel;
	public static JBusyComponent<JButton> busyComponent;
	
	public SimSeite(int width, int height, JFrame frame)
	{
		
		simPanel = new JPanel();
		simPanel.setSize(width, height);
		simPanel.setBorder(BorderFactory.createTitledBorder(UIManager.getBorder("TitledBorder.border"),"Simulation"));
		simPanel.setLayout(null);
		
		//Buttons zum Starten, Abbrechen und Pausieren der Simulation
		btnStart = new JButton();
		btnStart.setText("Start");
		btnStart.setBounds(10,365,180,50);
		busyComponent = new JBusyComponent<JButton>(btnStart);
		btnStart.addActionListener(new ActionListener()
		{
			public void actionPerformed( ActionEvent e )
			{
			    simulationstarten(); 
			}
		});
		simPanel.add(btnStart);	
	}
	
	private void simstarten()
	{
		 new Thread()
	      {
	        @Override
	        public void run()
	        {
	        	Start.start();
	        }
	      }.start();
	}
	public JButton getbtnStart()
	{
		return btnStart;
	}
	
	public JPanel getSimPanel() {
		return simPanel;
	}
}

und die dazugehörige Start.start() schaut so aus:

Java:
public class Start {
	
	public static void simulationsstart()
	{
		
		de.ballibum.SimSeite.busyComponent.setBusy(true);
		int i = 0;
		while (i<100)
		{
			System.out.println(i);
			i++;
		}
		System.out.println("Checken der Eingabedateien");
		System.out.println("Simulation wurde gestartet");
		System.out.println("Simulations erfolgreich beendet");
		de.ballibum.SimSeite.busyComponent.setBusy(false);
	}


}

Wenn ich das ganze ausführe wird die eigentlich Funktion ausgeführt und zusätzlich bekomme ich den folgenden Fehler:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at org.jdesktop.jxlayer.plaf.ext.LockableUI.setLocked(Unknown Source)
at org.divxdede.swing.busy.ui.AbstractBusyLayerUI.updateUIImpl(AbstractBusyLayerUI.java:117)
at org.divxdede.swing.busy.ui.BasicBusyLayerUI.updateUIImpl(BasicBusyLayerUI.java:438)
at org.divxdede.swing.busy.ui.AbstractBusyLayerUI.updateUI(AbstractBusyLayerUI.java:97)
at org.divxdede.swing.busy.ui.BasicBusyLayerUI$AnimationObserver.update(BasicBusyLayerUI.java:739)
at java.util.Observable.notifyObservers(Unknown Source)
at java.util.Observable.notifyObservers(Unknown Source)
at org.divxdede.swing.busy.icon.AbstractBusyIcon.repaint(AbstractBusyIcon.java:366)
at org.divxdede.swing.busy.icon.AbstractBusyIcon$ModelListener.stateChanged(AbstractBusyIcon.java:500)
at javax.swing.DefaultBoundedRangeModel.fireStateChanged(Unknown Source)
at org.divxdede.swing.busy.DefaultBusyModel.fireStateChanged(DefaultBusyModel.java:238)
at org.divxdede.swing.busy.DefaultBusyModel$1.run(DefaultBusyModel.java:232)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(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)

Nur leider kann ich mit dem nicht viel anfangen.
mfg
Balli
p.s. vielen Dank für eure Unterstützung
 

KrokoDiehl

Top Contributor
Ich sehe nirgends, dass die BusyComponent dem Panel hinzugefügt (
Code:
add()
) wird. Ich kenne diese Komponente zwar nicht, aber ich denke dies gehört wie bei allen Swing/AWT-Komponenten dazu.
 

bERt0r

Top Contributor
Null layout, public static Membervariablen, GUI-Änderungen ausserhalb vom EDT... bei dem Stil dreht sich einem ja der Magen um. Dein Code sollte laufen, wenn du dein setBusy in ein EventQueue.invokeLater(new Runnable(){public void run(){...}}); packst.
 

ballibum

Aktives Mitglied
Hi danke für die Antworten.
Ich habe das ganze auch schon über getter und setter-Methoden probiert jedoch hat auch da das ganze nicht funktioniert, ich bin gerade dabei es wieder umzustellen, weil ich auch da das add(busy..) vergessen hatte.
Die Kritik am null Layout kann ich jedoch nicht nachvollziehen!!Was ist daran so falsch, so kann ich die Komponenten die ich habe exakt da positionieren, wo ich sie haben möchte.
mfg
Balli
 

bERt0r

Top Contributor
Null Layout is evil. Es ist viel mehr Aufwand für dich und ist fehleranfällig. Es kann nicht auf Größenänderungen reagieren. Schau dir den Link in meiner Sig an.

Von gettern und setter habe ich übrigens nicht gesprochen. Du solltest sämtliche Methodenaufrufe von SwingKomponenten (insbesondere wenns um so dinge wie setBusy geht) auf dem Event Dispatch Thread durchführen. Ansonsten gibts Thread Probleme.
 
Zuletzt bearbeitet:

ballibum

Aktives Mitglied
Okay, ich habe deinen Link durchgearbeitet und denke dass du bei großen Projekten durchaus recht hast, in diesem Fall habe ich so aber einfach einen eindeutigen Überblick. Werde die Umstellung dann aber mal in Angriff nehmen.
Deinen zweiten Punkt verstehe ich noch nicht ganz.
Ich habe einen Thread mit dem ich die GUI erzeuge und mit ihr arbeite. Von dieser GUI erzeuge ich einen neuen Thread, so lange wie der arbeitet, soll der Button Start nicht nutzbar sein.
Das heisst der Aufruf setBusy(true/false), sollte wenn möglich in dem GUI Thread bleiben. Habe ich das richtig verstanden?
mfg
Balli
 

ballibum

Aktives Mitglied
Also eigentlich müsste ich das ganze ja nun im richtigen Thread ausführen:

So starte ich meine Anwendung:


Java:
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import de.gui.HauptContainer;

public class Start {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//Laden der Konfigurationen
		//Ordnerobjektbeinhaltet den Ordnerpfad in dem sich alles befindet
		//Ordner ordner = new Ordner();
		//Starten der GUI
		try
		{
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		}
		catch(UnsupportedLookAndFeelException e)
		{
			System.out.println(e+"LookAndFeel wird nicht unterstützt");
		}
		catch(ClassNotFoundException e)
		{
			System.out.println(e+"Klasse nicht gefunden");
		} 
		catch(InstantiationException e)
		{
			System.out.println(e+"Fehler beim Starten");
		}
		catch(IllegalAccessException e)
		{
			System.out.println(e+"kein Zugriff");
		}
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				JFrame mPage = new HauptContainer().getmContainer();
				mPage.setVisible(true);
			}
		});	
	}

}

Und im Hauptcontainer sind eben alle JPanels auf gelistet. Und in einem JPanel gibt es eben den Button Start, mit welchem eine Funktion gestartet wird, welche längere Zeit in Anspruch genommen wird.
Für diese Zeit soll der Button ausgegraut werden, welches mir in einer vorherigen Version auch gelungen ist, jedoch habe ich da alle Komponenten in einer Klasse.

So hier nochmal die das Panel in welchem der button und die busyComponente sind unddie zeitintensive Funktion gestartet wird:

Java:
public class SimSeite {
	
	JPanel simPanel;
	JScrollPane statusausgabe;
	JScrollPane scrolltree;
	JTabbedPane tabbedPane;
	JProgressBar progressBar;
	DefaultTreeModel treemodel;
	JButton btnStart;
	public static JBusyComponent<JButton> busyComponent;
	
	public SimSeite(int width, int height, JFrame frame)
	{
		
		simPanel = new JPanel();
		simPanel.setSize(width, height);
		simPanel.setBorder(BorderFactory.createTitledBorder(UIManager.getBorder("TitledBorder.border"),"Simulation"));
		simPanel.setLayout(null);
				
		//Buttons zum Starten, Abbrechen und Pausieren der Simulation
		btnStart = new JButton();
		btnStart.setText("Start");
		btnStart.setBounds(10,365,180,50);
		busyComponent = new JBusyComponent<JButton>(btnStart);
		btnStart.addActionListener(new ActionListener()
		{
			public void actionPerformed( ActionEvent e )
			{
			    simulationstarten(); 
			}
		});
		simPanel.add(busyComponent);
		simPanel.add(btnStart);	
		
	}
	//hier die entsprechende Funktion:::
	private void simulationstarten()
	{
		 //busyComponent.setBusy(true);	
		 new Thread()
	      {
	        @Override
	        public void run()
	        {
	        	Start.simulationsstart();
	        }
	      }.start();
	     // busyComponent.setBusy(false);
	}
	
	public JPanel getSimPanel() {
		return simPanel;
	}
}

Und ich verstehe jetzt nicht wirklich wo mein Fehler liegt.
vielen Dank für eure Mühen
balli
 
Zuletzt bearbeitet:

bERt0r

Top Contributor
Du kennst dich anscheinen mit Threads noch nicht so richtig aus.
Java:
private void simulationstarten()
    {
         //busyComponent.setBusy(true); 
         new Thread()
          {
            @Override
            public void run()
            {
                Start.simulationsstart();
            }
          }.start();
         // busyComponent.setBusy(false);
    }

Hier liegen die setBusy aufrufe jetzt richtig auf dem EDT, sie werden aber sofort hintereinander durchgeführt. Du startest zwar zwischen drinnen den Thread, die Berechnung läuft aber dann parallel. Das heißt der Thread mit der Berechnung ist noch nicht fertig, da setzt du busy schon wieder auf falsch.
Am besten du verwendest einfach einen SwingWorker, mit dem hast du das EDT Problem nicht.
 

ballibum

Aktives Mitglied
ja , da hast du vollkommen recht, threads kenne ich noch nicht so lang. Vorallem ist dann das Problem nicht genau zu wissen wonach ich suchen muss/sollte.
Ich werde es mit dem SwingWorker mal probieren.
vielen Dank für deine Hilfe
mfg
Balli


OK, ich sehe schon das wird eine schwierige Geburt....
 

bERt0r

Top Contributor
Du kannst diesen Code in einen SwingWOrker packen, das sollte dann funktionieren:
Java:
 de.ballibum.SimSeite.busyComponent.setBusy(true);
        int i = 0;
        while (i<100)
        {
            System.out.println(i);
            i++;
        }
        System.out.println("Checken der Eingabedateien");
        System.out.println("Simulation wurde gestartet");
        System.out.println("Simulations erfolgreich beendet");
        de.ballibum.SimSeite.busyComponent.setBusy(false);
 

ballibum

Aktives Mitglied
Es tut mir tierisch Leid, aber ich raffe es einfach nicht, eventuell ist es für mich auch einfach zu spät(ich stehe gegen 4:30 auf) um nachmittags einfach zu programmieren.

Ich hatte es jetzt kurzfristig so versucht:
Java:
private void simulationstarten()
	{
		Thread simulation = new Thread()
	      {
	        @Override
	        public void run()
	        {
	        	Start.simulationsstart();
	        }
	      };
	      simulation.start();
	     if(simulation.getState()!=State.RUNNABLE)
	     {
	    	 btnStart.setText("Start");
				btnStart.setIcon(null);
				btnStart.setEnabled(true);
	     }
	     else
	     {
	    	 btnStart.setText("");
				btnStart.setIcon(new ImageIcon(sysInfo.getImagepfad()+"loadinfo.gif"));
				btnStart.setEnabled(false);
	    	
	     }
	    	 
	      
	}

aber auch das geht daneben. Ich werde mir das mit den SwingWorkern einfach mal in Ruhe durchtesten. Das Problem mit den SwingWorkern scheint zu sein, das mir der Einstieg fehlt. Ich weiß nicht genau wo meine Funktion hin muss und wie sie dem Button zugeführt wird (also von der Stelle her).

vielen Dank
Balli
 

ballibum

Aktives Mitglied
Okay, vielen Dank für die Hilfe mit dem SwingWorkerThread habe ich es hinbekommen, ich denke so schwer sind Threads gar nicht.
Den erfolgreichen Codeschnipsel poste ich heute Abend.
mfg
Balli
 

ballibum

Aktives Mitglied
Also ich habe es jetzt nach diesem Prinzip realisiert:

Java:
package test.workerthread.gui;

import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;

public class SwingWorkerThreadTest
{
	
	JFrame frame;
	private JButton btnStart = new JButton("Start");
	private JTextArea textArea = new JTextArea(25,25);
	private JScrollPane scrollPane = new JScrollPane(textArea);
	
	SwingWorkerThreadTest()
	{
		frame = new JFrame();
		frame.setSize(500,500);
		
		
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		textArea.setEditable(false);
		
		
		JPanel content = new JPanel();
		content.setLayout(new FlowLayout());
		content.add(scrollPane);
		content.add(btnStart);
		frame.setContentPane(content);
		frame.pack();
		
		ActionListener al = new ActionListener()
		{	
			@Override
			public void actionPerformed(ActionEvent e) 
			{
				new TestFunktionSwingWorker().execute();
			}
		};
		btnStart.addActionListener(al);
	}

	public JFrame getFrame()
	{
		return frame;
	}
	
	
	class TestFunktionSwingWorker  extends SwingWorker<Long,Object>{

		@Override
		protected Long doInBackground() throws Exception {
			
			btnStart.setText("Läuft");
			textArea.append("gestartet\n");
			btnStart.setEnabled(false);
			new TestFunktion().TestFunktion();
			return null;
		}
		@Override
		protected void done()
		{
			try
			{
				textArea.append("beendet\n");
				btnStart.setText("Start");
				btnStart.setEnabled(true);
			}
			catch(Exception e)
			{
				System.out.println(e);
			}
		}
		@Override
		protected void process(List<V> chunks)
		{
			
		}

	}
	
}
package test.workerthread.gui;

public class TestFunktion {
	
	public void TestFunktion()
	{
		for(int i = 0; i<99999;i++)
		{
			System.out.println(i);
		}
	}

}

Ist es nun möglich, auf die System.out.println()-Funktion zu reagieren. Ich denke mir, wenn ichd as schon so intensiv durcharbeite, will ich es auch gleich richtig verstehen.
mfg
balli
 

ballibum

Aktives Mitglied
Ich habe das ganze jetzt auch nochmal für die invokeLater()-Variante gemacht:

Java:
package test.invokelater.gui;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;


public class InvokeTest {

	JFrame frame;
	private static JButton btnStart = new JButton("Start");
	private static JTextArea textArea = new JTextArea(25,25);
	private JScrollPane scrollPane = new JScrollPane(textArea);
	
	InvokeTest()
	{
		frame = new JFrame();
		frame.setSize(500,500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		textArea.setEditable(false);
		
		JPanel content = new JPanel();
		content.setLayout(new FlowLayout());
		content.add(scrollPane);
		btnStart.addActionListener(new ButtonStart());
		content.add(btnStart);
		
		
		frame.setContentPane(content);
		frame.pack();
	}
	
	static class ButtonStart implements ActionListener
	{

		@Override
		public void actionPerformed(ActionEvent e)
		{
			new Thread (new Runnable()
			{
				@Override
				public void run()
				{
					btnStart.setText("Läuft");
					textArea.append("gestartet\n");
					btnStart.setEnabled(false);
					new TestFunktion().TestFunktion();
					SwingUtilities.invokeLater(new Runnable()
					{
						@Override
						public void run()
						{
							textArea.append("beendet\n");
							btnStart.setText("Start");
							btnStart.setEnabled(true);
						}
					});
				}
			}).start();
			
		}
		
	}
	public JFrame getFrame()
	{
		return frame;
	}
	
}

Es wird langsam*G*
Was ich jetzt gerne noch hätte wäre die Ausgabe des System.println() aus der Funktion in dem Textfeld. Gibt es da eine Möglichkeit, dass zu realisieren und trotzdem nicht allzu harsche Kritik von euch zu bekommen*G*?

mfg
Balli

Ansonsten vielen Dank für die tolle Unterstützung-so lerne ich von Tag zu Tag dazu!
 

ballibum

Aktives Mitglied
Okay das würde einfach gehen indem ich der Funktion die textArea übergebe:

Java:
static class ButtonStart implements ActionListener
	{

		@Override
		public void actionPerformed(ActionEvent e)
		{
			new Thread (new Runnable()
			{
				@Override
				public void run()
				{
					btnStart.setText("Läuft");
					textArea.append("gestartet\n");
					btnStart.setEnabled(false);
					new TestFunktion().TestFunktion(textArea);
					SwingUtilities.invokeLater(new Runnable()
					{
						@Override
						public void run()
						{
							textArea.append("beendet\n");
							btnStart.setText("Start");
							btnStart.setEnabled(true);
						}
					});
				}
			}).start();
			
		}
Aber ist das auch wirklich sinnvoll????


mit freundlichem Gruß
balli
 

Melfis

Aktives Mitglied
Schaut richtig aus bis auf:
Java:
                    btnStart.setText("Läuft");
                    textArea.append("gestartet\n");
                    btnStart.setEnabled(false);
Das muss auch in ein Runnable mit invokeLater() oder du setzt den text in deinem ActionListener.

MFG Melfis
 

ballibum

Aktives Mitglied
Okay, so wie ich es jetzt mache, wird doch aber mit dem Button-Klick, der Button funktionsunfähiggemacht und gewartet bis die Funktion abgeabreitet ist.
Warum muss ich dass dann in ein eigenes invokeLater machen? Damit, erst der Button disable gesetzt wird?
mfg
balli
 

Melfis

Aktives Mitglied
Weil du an der Stelle nicht im EDT bist, du bist da im "new Thread".
Änderungen an Swing-Componenten sind nur aus dem EDT zulässig.
 

ballibum

Aktives Mitglied
Okay, dann habe ich aber das Problem dass die textArea ausserhalb des EDT verändert wird, denn diese wird ja der TestFunktionTestFunktion(textArea) übergeben. Und wenn ich das wie vorgeschlagen mache, dann setzt die Änderung der textArea erst ein, wenn über die Funktion schon einige Werte in der TextArea ausgegeben werden.
mfg
Balli
 

Melfis

Aktives Mitglied
Java:
public class TestFunktion {
    
    public void TestFunktion(JTextArea textArea)
    {
        for(int i = 0; i<99999;i++)
        {
        SwingUtilities.invokeLater(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                        textArea.setText("i=\t"+i);
                        }
           });
        }
    }
 
}

Ich glaube da bietet sich eher der Swingworker mit den Methode publish&process() an.
 
Zuletzt bearbeitet:

Michael...

Top Contributor
Wenn der Inhalt der TextArea während der Simulation aktualisiert werden soll, muss die Simualtionsmethode mehr oder weniger direkt über eine Referenz auf diese zugreifen können.

Die Methode append() ist zwar als "thread safe" deklariert und könnte daher auch ausserhalb des EDT aufgerufen werden.
Dennoch kann das manchmal ungeahnte Auswirkungen haben.
z.B. auf eine JScrollPane, welche die JTextArea beinhaltet. Hier mal eine Demo des Unterschieds wenn die Methode über invokeLater im EDT ausgeführt wird und wenn nicht.
Java:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;


public class TextAreaUpdate {
	private JButton button1, button2;
	private JTextArea area;
	private JPanel panel;
	
	public TextAreaUpdate() {
		panel = new JPanel(new BorderLayout());
		area = new JTextArea();
		panel.add(new JScrollPane(area), BorderLayout.CENTER);
		button1 = new JButton("Start appending in Thread");
		button2 = new JButton("Start appending in EDT");
		panel.add(button1, BorderLayout.NORTH);
		panel.add(button2, BorderLayout.SOUTH);
		
		button1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				startSimualtion(false);
			}
		});
		button2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				startSimualtion(true);
			}
		});
	}
	
	private void startSimualtion(final boolean inEDT) {
		button2.setEnabled(false);
		area.setText("Starte Lesevorgang");
		new Thread(new Runnable() {
			private int i;
			public void run() {
				for (i=0; i<20; i++) {
					try {
						Thread.sleep(300);
						if(inEDT)
							appendInEDT("Zeile " + i);
						else
							appendInThread("Zeile " + i);
							
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				if(inEDT)
					appendInEDT("Lesevorgang beendet");
				else
					appendInThread("Lesevorgang beendet");
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						button2.setEnabled(true);
					}
				});
			}
		}).start();
	}
	
	public void appendInThread(String line) {
		area.append("\n" +line);
	}
	
	public void appendInEDT(final String line) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				area.append("\n" +line);
			}
		});
	}
	
	public Component getPanel() {
		return panel;
	}
	
	public static void main(String[] s) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				JFrame frame = new JFrame();
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setBounds(0, 0, 200, 200);
				frame.add(new TextAreaUpdate().getPanel());
				frame.setVisible(true);
			}
		});
	}
}
 

ballibum

Aktives Mitglied
Okay, vielen lieben Dank für eure Hilfestellungen und Korrekturen. Ich denke ich abe es jetzt und werde einfach alles einmal ausprobieren, publish() und process) scheint ja nochmal eine eigene Welt zu sein.
vielen Dank
Balli
 

Neue Themen


Oben