Thread-Problem mit der Darstellung beim Sperren des Fensters

hansmueller

Bekanntes Mitglied
Hallo,

ich möchte bei meinen Programm das Fenster nach einer Benutzereingabe (z. B. Button oder Menu) komplett sperren, dann die Berechnungen ausführen lassen und dann wieder das Fenster entsperren.

Ich habe dafür eine spezielle Klasse, die als GlasPane fungiert, sämtliche Eingaben abfangen kann, den Inhalt des Fensters leicht eingraut und ein Textfeld mit einer Nachricht besitzt.

Java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 *  <p>HKGlassPane</p>
 *  Eine HilfsKlasse, die dafür sorgen soll, daß das Hauptfenster nicht mehr
 *  bedient werden kann, wenn eine rechenintensive Aktion ausgeführt wird.
 *
 *  
 *  @author hansmueller
 *  
 */
public class HKGlassPane extends JComponent
{
	Border borderNachrichtenRahmen;
	
	JLabel lbNachricht;
	Color colNachrichtenHintergrund;
	Color colGlassPaneHintergrund;
	Color colGrundfarbe;
	
	Boolean bAktiviert = false;
	
	GlassPaneTastaturListener gptl;
	String strNachrichtentext;
	
	public HKGlassPane()
	{
		lbNachricht = new JLabel();//Wichtig, damit es zu keiner Nullpoint-Ecxeption beim setBackground kommt.
		
		setOpaque(false);
		colGrundfarbe = UIManager.getColor("inactiveCaptionBorder");
		
		colGlassPaneHintergrund = new Color(colGrundfarbe.getRed(), colGrundfarbe.getGreen(), colGrundfarbe.getBlue(), 128);
		
		setBackground(colGlassPaneHintergrund);
		setLayout(new GridBagLayout());

		//Das Nachrichten-JLabel:
		
		add(lbNachricht, new GridBagConstraints());
		lbNachricht.setOpaque(true);
		borderNachrichtenRahmen = new EmptyBorder(10, 10, 10, 10);
		lbNachricht.setBorder(borderNachrichtenRahmen);

		//Maus-, Tastatur- und Fokus-Events müssen abgefangen werden.

		addMouseListener(new MouseAdapter(){});
		addMouseMotionListener(new MouseMotionAdapter(){});

		gptl = new GlassPaneTastaturListener();

		setFocusTraversalKeysEnabled(false);
	}

	/**
	 * Die HKGlassPane ist zwar transparent, aber um dem Fenster ein "gesperrtes" Aussehen
	 * zu geben, muß der Hintergrund gezeichnet werden.
	 */
	@Override
	protected void paintComponent(Graphics g)
	{
		g.setColor(getBackground());
		g.fillRect(0, 0, getSize().width, getSize().height);
	}

	/**
	 * Die Hintergrundfarbe des Nachrichten-JLabels ist die selbe wie beim GlassPane,
	 * jedoch ohne den Alpha-Kanal. (Dadurch wird es nicht transparent).
	 */
	@Override
	public void setBackground(Color c)
	{
		super.setBackground(c);

		colNachrichtenHintergrund = new Color(c.getRGB());
		//System.out.println(c.toString());
		lbNachricht.setBackground(colNachrichtenHintergrund);
	}
	
	/**
	 * Diese Methode ist dafür zuständig die Tastatur zu blockieren und wieder freizugeben.
	 */
	public void mbehandleAWTEventListener()
	{
		if(bAktiviert)
		{
			Toolkit.getDefaultToolkit().addAWTEventListener(gptl, AWTEvent.KEY_EVENT_MASK);
			/*Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener()
			{
				public void eventDispatched(AWTEvent e)
				{
					if (e instanceof KeyEvent)
					{
						((KeyEvent) e).consume();
					}
				}
			}, AWTEvent.KEY_EVENT_MASK);*/
		}
		else
		{
			Toolkit.getDefaultToolkit().removeAWTEventListener(gptl);
		}
	}

	/**
	 * Macht das Fenster unbenutzbar und verändert den Mauszeiger in eine Sanduhr.
	 * @param p_Nachrichtentext Der Text wird mittig im Fenster angezeigt.
	 * 
	 */
	public void mAktiviereGlasPane(String p_Nachrichtentext)
	{	
		if (strNachrichtentext != null && strNachrichtentext.length() > 0)
		{
			lbNachricht.setVisible(true);
			lbNachricht.setText(strNachrichtentext);
			lbNachricht.setForeground(getForeground());
		}
		else
		{
			lbNachricht.setVisible(false);
		}
			
		bAktiviert = true;
		
		setVisible(true);
		
		setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		mbehandleAWTEventListener();
		
		requestFocusInWindow();		
		
	}

	/**
	 * Macht das Fenster wieder benutzbar
	 */
	public void mDeaktiviereGlasPane()
	{
		bAktiviert = false;
		mbehandleAWTEventListener();
		setCursor(null);
		setVisible(false);
	}
	
	/**
	 * Diese Klasse des AWTEventListeners enthält nur eine Methode, die sämtliche Eingaben über
	 * Tastatur ins Nirvana leitet.
	 */
	public class GlassPaneTastaturListener implements AWTEventListener
	{
		public void eventDispatched(AWTEvent event)
		{
			if (event instanceof KeyEvent)
			{
				((KeyEvent) event).consume();
			}			
		}		
	}
}

Wenn ich die Methoden mAktiviereGlasPane(String p_Nachrichtentext) oder mDeaktiviereGlasPane() aber benutze, werden diese nicht oder nur unvollständig ausgeführt.

Ich denke, daß liegt daran, daß der Grafik-Thread seine Aufgabe irgendwie nicht komplett abarbeitet.

Hier ein kleines Testprogramm, welches die obrige Klasse verwendet:
Java:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;


public class MainProg
{
	JFrame f;
	JButton b1;
	JButton b2;
	HKGlassPane glas;
	JMenuBar menuBar;
	JMenu menu;
	JMenuItem menuItem;
	ListenerButtons lb;
	
	public static void main(String[] args)
	{
		MainProg x = new MainProg();

	}
	
	public MainProg()
	{
		f = new JFrame("Test Glasspane");
		glas = new HKGlassPane();
		
		f.setGlassPane(glas);	
		
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		f.setLayout(new GridLayout(1, 2));
		f.setPreferredSize(new Dimension(400, 300));
		
		menuBar = new JMenuBar();
        menu = new JMenu("Menu");
        menuItem = new JMenuItem("Do nothing");
        menu.add(menuItem);
        
       
        menuBar.add(menu);
        f.setJMenuBar(menuBar);

		
		b1 = new JButton("Test 01");
		b2 = new JButton("Test 02");
		
		lb = new ListenerButtons(this);
		
		b1.addActionListener(lb);
		b2.addActionListener(lb);
		menuItem.addActionListener(lb);
		
		f.add(b1);
		f.add(b2);		
				
		f.pack();
		f.setVisible(true);
		
	}
	
	
		
	public class ListenerButtons implements ActionListener
	{

		HKGlassPane gp;
		MainProg M;
		
		public ListenerButtons(MainProg p_M)
		{
			gp = p_M.glas;
			M = p_M;
		}
		@Override
		public void actionPerformed(ActionEvent e)
		{
			if(e.getSource() == b1)
			{
				System.out.println("Knopf 01");
				
				gp.mAktiviereGlasPane("Bitte warten");
				
				for(Integer i = 0; i < 999999999; i++);
				{
					System.out.println("Was zum Arbeiten.");
					
				}
				
				gp.mDeaktiviereGlasPane();
			}
			
			if(e.getSource() == b2)
			{
				System.out.println("Knopf 02");
							
			}
			
			if(e.getSource() == M.menuItem)
			{
				System.out.println("Menü");
				
				gp.mAktiviereGlasPane("Bitte warten");
								
				for(Integer i = 0; i < 999999999; i++);
				{
					System.out.println("Was zum Arbeiten.");
					
				}
				
				gp.mDeaktiviereGlasPane();
			}
			
		}
		
		
	}

}

Wie kriege ich das Programm dazu, das Fenster zuerst zu sperren und erst dann die Berechnung durchzuführen?
Ich habe schon mit invokeLater() und invokeAndWait() herumexperimentiert, aber bin zu keinem brauchbaren Ergebnis gekommen.

Eigendlich wollte ich mit dem Sperren/Entsperren des Fensters verhindern, daß ich komplizierte Threat-Geschichten machen muß.

Weiß vielleicht einer von euch eine Lösung?
Evtl. eine SwingWorker-Sache?

MfG
hansmueller
 
Deine for Schleifen (die wohl die Berechnung simulieren sollen) müssen in einen separaten Thread ausgelagert werden, dieser muss dann am Ende der Berechnung die Deaktivierung des GlasPane vornehmen oder zumindest der GUI mitteilen, dass der GlasPane wieder deaktiviert werden kann.
 
Danke Michael,

ich werde es dann so machen, wie du es beschrieben hast.

(In meiner Klasse HKGlassPane war ein kleiner Fehler, deshalb poste ich jetzt die korregierte Fassung, nur für den Fall, daß jemand mal sowas gebrauchen kann.)

HKGlassPane:
Java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 *  <p>HKGlassPane</p>
 *  Eine HilfsKlasse, die dafür sorgen soll, daß das Hauptfenster nicht mehr
 *  bedient werden kann, wenn eine rechenintensive Aktion ausgeführt wird.
 *
 *  
 *  @author hansmueller
 *  
 */
public class HKGlassPane extends JComponent
{
	Border borderNachrichtenRahmen;
	
	JLabel lbNachricht;
	Color colNachrichtenHintergrund;
	Color colGlassPaneHintergrund;
	Color colGrundfarbe;
	
	Boolean bAktiviert = false;
	
	GlassPaneTastaturListener gptl;
	String strNachrichtentext;
	
	public HKGlassPane()
	{
		lbNachricht = new JLabel();//Wichtig, damit es zu keiner Nullpoint-Ecxeption beim setBackground kommt.
		
		setOpaque(false);
		colGrundfarbe = UIManager.getColor("inactiveCaptionBorder");
		
		colGlassPaneHintergrund = new Color(colGrundfarbe.getRed(), colGrundfarbe.getGreen(), colGrundfarbe.getBlue(), 128);
		
		setBackground(colGlassPaneHintergrund);
		setLayout(new GridBagLayout());

		//Das Nachrichten-JLabel:
		
		add(lbNachricht, new GridBagConstraints());
		lbNachricht.setOpaque(true);
		borderNachrichtenRahmen = new EmptyBorder(10, 10, 10, 10);
		lbNachricht.setBorder(borderNachrichtenRahmen);

		//Maus-, Tastatur- und Fokus-Events müssen abgefangen werden.

		addMouseListener(new MouseAdapter(){});
		addMouseMotionListener(new MouseMotionAdapter(){});

		gptl = new GlassPaneTastaturListener();

		setFocusTraversalKeysEnabled(false);
	}

	/**
	 * Die HKGlassPane ist zwar transparent, aber um dem Fenster ein "gesperrtes" Aussehen
	 * zu geben, muß der Hintergrund gezeichnet werden.
	 */
	@Override
	protected void paintComponent(Graphics g)
	{
		g.setColor(getBackground());
		g.fillRect(0, 0, getSize().width, getSize().height);
	}

	/**
	 * Die Hintergrundfarbe des Nachrichten-JLabels ist die selbe wie beim GlassPane,
	 * jedoch ohne den Alpha-Kanal. (Dadurch wird es nicht transparent).
	 */
	@Override
	public void setBackground(Color c)
	{
		super.setBackground(c);

		colNachrichtenHintergrund = new Color(c.getRGB());
		//System.out.println(c.toString());
		lbNachricht.setBackground(colNachrichtenHintergrund);
	}
	
	/**
	 * Diese Methode ist dafür zuständig die Tastatur zu blockieren und wieder freizugeben.
	 */
	public void mbehandleAWTEventListener()
	{
		if(bAktiviert)
		{
			Toolkit.getDefaultToolkit().addAWTEventListener(gptl, AWTEvent.KEY_EVENT_MASK);
			/*Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener()
			{
				public void eventDispatched(AWTEvent e)
				{
					if (e instanceof KeyEvent)
					{
						((KeyEvent) e).consume();
					}
				}
			}, AWTEvent.KEY_EVENT_MASK);*/
		}
		else
		{
			Toolkit.getDefaultToolkit().removeAWTEventListener(gptl);
		}
	}

	/**
	 * Macht das Fenster unbenutzbar und verändert den Mauszeiger in eine Sanduhr.
	 * @param p_Nachrichtentext Der Text wird mittig im Fenster angezeigt.
	 * 
	 */
	public void mAktiviereGlasPane(String p_Nachrichtentext)
	{	
		strNachrichtentext = p_Nachrichtentext;
				
		if (strNachrichtentext != null && strNachrichtentext.length() > 0)
		{			
			lbNachricht.setVisible(true);
			lbNachricht.setText(strNachrichtentext);
			lbNachricht.setForeground(getForeground());
		}
		else
		{
			lbNachricht.setVisible(false);
		}
			
		bAktiviert = true;
		
		setVisible(true);
		
		setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		requestFocusInWindow();	
		mbehandleAWTEventListener();		
	}

	/**
	 * Macht das Fenster wieder benutzbar
	 */
	public void mDeaktiviereGlasPane()
	{
		bAktiviert = false;
		mbehandleAWTEventListener();
		setCursor(null);
		setVisible(false);
	}
	
	/**
	 * Diese Klasse des AWTEventListeners enthält nur eine Methode, die sämtliche Eingaben über
	 * Tastatur ins Nirvana leitet.
	 */
	public class GlassPaneTastaturListener implements AWTEventListener
	{
		public void eventDispatched(AWTEvent event)
		{
			if (event instanceof KeyEvent)
			{
				((KeyEvent) event).consume();
			}			
		}		
	}
}

MainProg:
Java:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;

public class MainProg
{
	JFrame f;
	JButton b1;
	JButton b2;
	HKGlassPane glas;
	JMenuBar menuBar;
	JMenu menu;
	JMenuItem menuItem;
	ListenerButtons lb;
	
	public static void main(String[] args)
	{
		MainProg x = new MainProg();
	}
	
	public MainProg()
	{
		f = new JFrame("Test Glasspane");
		glas = new HKGlassPane();
		
		f.setGlassPane(glas);	
		
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		f.setLayout(new GridLayout(1, 2));
		f.setPreferredSize(new Dimension(400, 300));
		
		menuBar = new JMenuBar();
        menu = new JMenu("Menu");
        menuItem = new JMenuItem("Do nothing");
        menu.add(menuItem);
        
       
        menuBar.add(menu);
        f.setJMenuBar(menuBar);

		
		b1 = new JButton("Test 01");
		b2 = new JButton("Test 02");
		
		lb = new ListenerButtons(this);
		
		b1.addActionListener(lb);
		b2.addActionListener(lb);
		menuItem.addActionListener(lb);
		
		f.add(b1);
		f.add(b2);		
				
		f.pack();
		f.setVisible(true);				
	}	
		
	public class ListenerButtons implements ActionListener
	{
		HKGlassPane gp;
		MainProg M;
		
		public ListenerButtons(MainProg p_M)
		{
			gp = p_M.glas;
			M = p_M;
		}
		@Override
		public void actionPerformed(ActionEvent e)
		{
			if(e.getSource() == b1)
			{
				System.out.println("Knopf 01");
				
				gp.mAktiviereGlasPane("Bitte warten");
				
				new Thread(new Runnable()
				{
					@Override
					public void run()
					{
						for(Integer i = 0; i < 999999999; i++);
						{
							System.out.println("Was zum Arbeiten.");							
						}
						
						System.out.println("Fertig");
						gp.mDeaktiviereGlasPane();
					}
				}).start();				
			}
			
			if(e.getSource() == b2)
			{
				System.out.println("Knopf 02");							
			}
			
			if(e.getSource() == M.menuItem)
			{
				System.out.println("Menü");
				
				gp.mAktiviereGlasPane("Bitte warten");
			
				new Thread(new Runnable()
				{
					@Override
					public void run()
					{						
						for(Integer i = 0; i < 999999999; i++);
						{
							System.out.println("Was zum Arbeiten.");							
						}
						
						System.out.println("Fertig");
						gp.mDeaktiviereGlasPane();
					}
				}).start();				
			}			
		}		
	}
}

MfG
hansmueller
 

Zurück
Oben