.setEnabled(false) zu langsam?

Asgar13

Bekanntes Mitglied
Hallo,

ich habe ein Programm(siehe unten), welches einen ping macht (durch ping.sh), solange er pingt soll man keinen Button drücken können, sobald dieser vorbei ist, soll er alle Buttons wieder enablen.

Problem:
Wenn ich einen Doppelklick oder in der Ausführzeit(durch toten ping verlängert) einen Button drückt macht das Programm immer noch das andere Buttonevent.
Wo ist der Fehler?

ping,java,telnet,firmware, exitbutton sind alles Button
router3 ist ein Textfeld.
(Ja, unschöne for-schleife)

Java:
    		ping.addListener(SWT.Selection, new Listener(){
			public void handleEvent(Event e) {
				switch (e.type){
				case SWT.Selection:
					
					try{
						ping.setEnabled(false);
						exitbutton.setEnabled(false);
						telnet.setEnabled(false);
						java.setEnabled(false);
						firmware.setEnabled(false);
						String atftp[]={"/bin/sh", "-c", "expect/ping.sh "+router3.getText()};
						Process d = Runtime.getRuntime().exec(atftp);
						BufferedReader in = new BufferedReader( new InputStreamReader((d.getInputStream())));
						int i=0;
						for (String pingbuffer;(pingbuffer = in.readLine()) != null;)
						{
							control.add(pingbuffer);
							if(pingbuffer.equals("Erfolgreich"))
							{
								check.add("Router ist erreichbar");
								i++;
							}
						}
						if(i==0)//Falls ein Fehler da ist, schreibe...
						{
							check.add("Fehler: Routers wurde nicht erreicht");
						}
						in.close();
						
						BufferedReader is = new BufferedReader( new InputStreamReader((d.getErrorStream())));
						for (String pingbuffer;(pingbuffer = is.readLine()) != null;control.add(pingbuffer));
						is.close();
						control.add("");
					}
					catch(IOException ioexp){
					control.add("Fehler bei dem IOExecption");
					}
					catch(Exception exp){
					control.add("Fehler bei dem Execption");
					}
					ping.setEnabled(true);
					telnet.setEnabled(true);
					firmware.setEnabled(true);
					java.setEnabled(true);
					exitbutton.setEnabled(true);
					}
				}
			});
 
Danke für die Antwort, das ich waitFor(); vergessen habe, war dumm, aber leider auch nicht die Problemlösung

EDIT1:falls es weiter hilft, BS ist linux

EDIT2: sieht für mich so auf, als überspringe er die setEnabled fürs erste und macht diese später
 
Zuletzt bearbeitet:
Java:
public class test {

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		try {
			String[] cmd = { "cmd.exe", "/c", "ping localhost" };
			Process m;
			String S = "";

			m = Runtime.getRuntime().exec(cmd);
			m.waitFor();
			System.out.println("fertig.");
			BufferedReader in = new BufferedReader(new InputStreamReader(
					m.getInputStream()));

			while ((S = in.readLine()) != null) {
				System.out.println(S);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}

}

Java:
m.waitFor();

funktioniert bei mir.
 
@Volvagia

1. Habe einen SWT-Listener Header aus einem Tutor kopiert, deshalb sieht der so komisch aus.
2. Wenn die Maschine die Sache abarbeitet, dann soll diese als erstes die Buttons auf (false) setzen, dann 10 Sekunden lang (weil ich pings ins leere laufen lasse) arbeiten(dort sollten die Buttons nicht anklickbar sein) und dann die Buttons wieder anklickbar machen.

@chalkbag
waitFor() selbst funktioniert, aber die Buttons disablen sich ímmer noch nicht.
 
Ach so, ich hab das SWT überlesen. 😳
Mit SWT kenn ich mich nicht aus, aber das Problem könnte das Selbe sein. Unter AWT/Swing werden Standartimplementierte Listener und Zeichnen im selben Thread (dem sog. Event Dispatcher Thread) durchgeführt. Wärend der Listener abarbeitet, kann er deshalb nicht die neuen disableden Buttons zeichnen. Deshalb musst du im Listener einen Thread starten, der die Änderungen durchführt. Prüfe aber vorher, ob SWT Thread-Safe ist, wenn nicht musst du GUI-Änderungen per EventQueue/SwingUtilities#invokeLater oder in vereinzelten Fällen invokeAndWait ausführen lassen.
 
so ein problem in der art kommt vor, die buttons werden nicht sofort disabled, weil die operation anscheind zeit braucht.

umgehen kann man das in dem man es mit einem simplen boolean blockt anstatt sich nur auf die buttons zu verlassen. so könnte man verhindern, das die methode mehr als einmal läuft

Java:
boolean click = true;


handle() {
  if click == true {
    click = false;
    buttons.disable();
    foo();
    click = true;
  }
}
click darf es dabei nur einmal global geben
 
So wie ich das sehe, willst du alles in einem Thread machen, was zu bekanntem Resultat führt. Du müsstest die Gui und die Logik (exec) in separaten Threads ausführen.

Hier ein Beispiel wie es gehen würde.


Java:
import java.awt.EventQueue;


public class testgui implements ActionListener{

	private JFrame frame;
	private JButton button;
	private JButton button_1;
	private JButton button_2;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					testgui window = new testgui();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public testgui() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 450, 300);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		button = new JButton("1");
		button.addActionListener(this);
		frame.getContentPane().add(button, BorderLayout.NORTH);
		
		button_1 = new JButton("2");
		button_1.addActionListener(this);
		frame.getContentPane().add(button_1, BorderLayout.WEST);
		
		button_2 = new JButton("3");
		button_2.addActionListener(this);
		frame.getContentPane().add(button_2, BorderLayout.EAST);
	}

	@Override
	public void actionPerformed(ActionEvent event) {
			setButtonGroup(false);
			
			
			Thread t = new Thread() {
				
				@Override
				public void run() {
					try {
			            String[] cmd = { "cmd.exe", "/c", "ping localhost" };
			            Process m;
			            String S = "";
			 
			            m = Runtime.getRuntime().exec(cmd);
			            //m.waitFor();
			            System.out.println("fertig.");
			            BufferedReader in = new BufferedReader(new InputStreamReader(
			                    m.getInputStream()));
			 
			            while ((S = in.readLine()) != null) {
			                System.out.println(S);
			            }
			        } catch (Exception ex) {
			            ex.printStackTrace();
			        }
			        setButtonGroup(true);
				}
			};
			t.start();
		
	}

	private void setButtonGroup(boolean b) {
		button.setEnabled(b);
		button_1.setEnabled(b);
		button_2.setEnabled(b);
		
	}

}

[Edit] war ich langsam 😳
 
Kurze Verständnisfrage:

invokeLater->Wiederholt den Prozess irgendwann später

invokeAndWait->Wiederholt Prozess und macht dann den nächsten weiter

Verstehe ich das so richtig?

Jain.
Es gibt eine Queue, in die Runnable-Instanzen eingeordnet werden. Der EDT arbeitet einfach nacheinander die run-Methode der Instanzen ab. Later reiht dabei die Runnable ein, und läuft dann ganz normal weiter, unabhängig davon, wann der EDT wirklich etwas damit macht. AndWait reiht die Instanz ein und wartet anschließend, bis die Runnable abgearbeitet wurde. Das kostet aber natürlich je nach dem ziemlich viel Zeit, und wird in den seltensten Fällen gebraucht.

Da ich weiß, das ich auf solche Sachen manchmal wärend des Codens vergesse, da ich mir wärend des Tippens über Dinge wie die weitere Klassenstruktur etc. Gedanken mache, füge ich an kritischen Punkten, die viel mit der GUI machen oft diesen Code ein:

Java:
if(!EventQueue.isDispatchThread())
	throw new IllegalAccessError("Warning: No Dispatcher!");

Das prüft, ob der aktuelle Thread der (bzw. ein) Dispatcher ist, und schmeißt eine Exception wenn nicht. So kann ich ganz einfach über den Stack zurückverfolgen, wo ich ein invokeLater vergessen habe.


Ein Programm zum testen:


Java:
import java.awt.EventQueue;

public class EDTTest
{
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				start();
			}
		});
	}
	private static void start()
	{
		System.out.println(Thread.currentThread().getName());
	}
}

Code:
AWT-EventQueue-0

Java:
public class EDTTest
{
	public static void main(String[] args)
	{
		start();
	}
	private static void start()
	{
		System.out.println(Thread.currentThread().getName());
	}
}

Code:
main
 
Lösungen getestet von chalkbag & bone2.

Quelltext hier:
Java:
//Instanzierung
static Display display = new Display();
static Shell shell = new Shell(display, SWT.NONE);
static Button ping=new Button(shell,SWT.NONE);
static Button telnet=new Button(shell,SWT.NONE);
static Button java=new Button(shell,SWT.NONE);
static Button firmware=new Button(shell, SWT.NONE);

  		//Event 
    		ping.addListener(SWT.Selection, new Listener(){
			public void handleEvent(Event e) {
				switch (e.type){
				case SWT.Selection:
					
					try{
						setDisable(false);
						//boolean click = true;

						/*if (click == true) {
						    click = false;
						    ping.setEnabled(false);
							exitbutton.setEnabled(false);
							telnet.setEnabled(false);
							java.setEnabled(false);
							firmware.setEnabled(false);
						    click = true;
						  }*/

						/*ping.setEnabled(false);
						exitbutton.setEnabled(false);
						telnet.setEnabled(false);
						java.setEnabled(false);
						firmware.setEnabled(false);*/
						//setButtonGroup(false);
						//String atftp[]={"/bin/sh", "-c", "expect/ping.sh "+router3.getText()};
						String atftp[]={"/bin/sh", "-c", "ping -c 5 192.168.2.214"+router3.getText()};
						Process d = Runtime.getRuntime().exec(atftp);
						d.waitFor();
						BufferedReader in = new BufferedReader( new InputStreamReader((d.getInputStream())));
						int i=0;
						for (String pingbuffer;(pingbuffer = in.readLine()) != null;)
						{
							control.add(pingbuffer);
							if(pingbuffer.equals("Erfolgreich"))
							{
								check.add("Router ist erreichbar");
								i++;
							}
						}
						if(i==0)//Falls ein Fehler da ist, schreibe...
						{
							check.add("Fehler: Routers wurde nicht erreicht");
						}
						in.close();
						
						BufferedReader is = new BufferedReader( new InputStreamReader((d.getErrorStream())));
						for (String pingbuffer;(pingbuffer = is.readLine()) != null;control.add(pingbuffer));
						is.close();
						control.add("");
					}
					catch(IOException ioexp){
					control.add("Fehler bei dem IOExecption");
					}
					catch(Exception exp){
					control.add("Fehler bei dem Execption");
					}
					ping.setEnabled(true);
					telnet.setEnabled(true);
					firmware.setEnabled(true);
					java.setEnabled(true);
					exitbutton.setEnabled(true);
					}
				}
			});

}//Ende Main

       //Thread/Funktion zum disablen 
	public static void setDisable(boolean f){
		ping.setEnabled(f);
		exitbutton.setEnabled(f);
		telnet.setEnabled(f);
		java.setEnabled(f);
		firmware.setEnabled(f);
		}
}

Bei bone2s Lösung ist bei mir nur der Doppelklick verhindert, was gut praktisch ist, aber nicht die ganze Lösung.

Bei chalkbags Lösung passiert das selbe, wie bei meinen ersten Post.(Scheint wohl nicht an Thread zu legen)

@Volvagia, ich versuche deinen Vorschlag als nächstes.

EDIT: Falls ich etwas falsch gemacht habe, bitte drauf hinweisen.
 
Zuletzt bearbeitet:
???:L

Habe mir jetzt eine Stunde lang Beispiele dazu angesehen und nicht verstanden, wie ich das einbauen soll.

Unter den disablen, um das disable, oder um den ganzen Prozess?
 
Ist doch nicht schwer, hier ein Beispiel auf Swing-Basis. Ist zwar sehr ähnlich zu chalks, aber vielleicht klappt es ja dieses mal.

Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Ping
{
	public static void main(String[] args)
	{
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		final JTextField router3 = new JTextField();
		final List<String> control = new LinkedList<String>();
		final List<String> check = new LinkedList<String>();

		final JButton pingButton = new JButton("Pingen");
		pingButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				new Thread() {
					public void run()
					{
						setButtonsEnabled(false);
						
						try
						{
							String atftp[]={"/bin/sh", "-c", "expect/ping.sh "+router3.getText()};
							Process d = Runtime.getRuntime().exec(atftp);
							BufferedReader in = new BufferedReader( new InputStreamReader((d.getInputStream())));
							int i=0;
							for (String pingbuffer;(pingbuffer = in.readLine()) != null;)
							{
								control.add(pingbuffer);
								if(pingbuffer.equals("Erfolgreich"))
								{
									check.add("Router ist erreichbar");
									i++;
								}
							}
							if(i==0)
							{
								check.add("Fehler: Routers wurde nicht erreicht");
							}
							in.close();
							
							BufferedReader is = new BufferedReader( new InputStreamReader((d.getErrorStream())));
							for (String pingbuffer;(pingbuffer = is.readLine()) != null; control.add(pingbuffer));
							is.close();
							control.add("");
						}
						catch(IOException ioexp){
							control.add("Fehler bei dem IOExecption");
							ioexp.printStackTrace();
						}
						catch(Exception exp){
							control.add("Fehler bei dem Execption");
							exp.printStackTrace();
						}
						
						setButtonsEnabled(true);
					}
				}.start();
			}
			private void setButtonsEnabled(final boolean b)
			{
				SwingUtilities.invokeLater(new Runnable() {
					public void run()
					{
						pingButton.setEnabled(b);
					}
				});
			}
		});
		frame.add(pingButton);
		
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}
}
 
Ich habe zuerst versucht, wie bei dir die das Disabled zu invokeLater machen, dabei kam keine Veränderung, als ich dann das Enabled mit invokeLater machte, es funktionstechnisch funktioniert, kein Doppelklick, oder ein Klick auf die anderen Buttons möglich, doch das Aussehen(zu grau werden) fehlte, hast du dazu noch eine Idee?

Nochmal der Quelltext:
(SwingUtilities.invokeLater ist in SWT display.asyncExec)
Java:
ping.addListener(SWT.Selection, new Listener(){
			public void handleEvent(Event e) {
				switch (e.type){
				case SWT.Selection:
					
					
					//setDisable(false);
					try{
						
						ping.setEnabled(false);
						exitbutton.setEnabled(false);
						telnet.setEnabled(false);
						java.setEnabled(false);
						firmware.setEnabled(false);
						//String atftp[]={"/bin/sh", "-c", "expect/ping.sh "+router3.getText()};
						
						String atftp[]={"/bin/sh", "-c", "ping -c 5 192.168.2.214"};
						Process d = Runtime.getRuntime().exec(atftp);
						d.waitFor();
						BufferedReader in = new BufferedReader( new InputStreamReader((d.getInputStream())));
						int i=0;
						for (String pingbuffer;(pingbuffer = in.readLine()) != null;)
						{
							control.add(pingbuffer);
							if(pingbuffer.equals("Erfolgreich"))
							{
								check.add("Router ist erreichbar");
								i++;
							}
						}
						if(i==0)//Falls ein Fehler da ist, schreibe...
						{
							check.add("Fehler: Routers wurde nicht erreicht");
						}
						in.close();
						
						BufferedReader is = new BufferedReader( new InputStreamReader((d.getErrorStream())));
						for (String pingbuffer;(pingbuffer = is.readLine()) != null;control.add(pingbuffer));
						is.close();
						control.add("");
					}
					catch(IOException ioexp){
					control.add("Fehler bei dem IOExecption");
					}
					catch(Exception exp){
					control.add("Fehler bei dem Execption");
					}
					setEnable(true);
					/*ping.setEnabled(true);
					telnet.setEnabled(true);
					firmware.setEnabled(true);
					java.setEnabled(true);
					exitbutton.setEnabled(true);*/
					
					}
				}
			});

Java:
	public static void setDisable(final boolean f){
		display.asyncExec(new Runnable () {
			public void run() {
        		ping.setEnabled(f);
        		exitbutton.setEnabled(f);
        		telnet.setEnabled(f);
        		java.setEnabled(f);
        		firmware.setEnabled(f);
            }
            });

		}

Java:
	public static void setEnable(final boolean f){
		display.asyncExec(new Runnable () {
			public void run() {
        		ping.setEnabled(f);
        		exitbutton.setEnabled(f);
        		telnet.setEnabled(f);
        		java.setEnabled(f);
        		firmware.setEnabled(f);
            }
            });

		}
 
Was du beachten musst, dass Volvagia sowie mein Beispiel die Buttons im separatem Thread aktiviert, nicht im Gui Thread. Ich kann mich eigentlich nicht vorstellen, wenn du das so übernommen hast, dass es nicht funktionieren würde.

Die Lösung ist zwar nicht ganz so schön, schöner wäre es wohl z.b. über eine synchronisierte Look-Variable. Aber laufen sollte es auf jeden Fall, zumindest ging mein Code bei mir wie gewünscht.
 
Du hast aber nach wie vor keinen Thread gestartet.

Java:
new Thread() {
	public void run() {
		//Hier pingen.
	};
}.start();

setDisable UND setEnable sind unnötig, da sie ja das Selbe machen. ^^ Da würde eine Methode reichen, du nur per false und später true aufrufst.
 
1. Danke an Volvagia, chalkbag, bone2.
2.

Ja, der Thread hatte gefehlt.

Dann kam die Fehlermeldung: invalid_thread_access

Lösung:
FAQ Why do I get an invalid thread access exception? - Eclipsepedia

Java:
//Event 
    		ping.addListener(SWT.Selection, new Listener(){
    			public void handleEvent(Event e) {
    				switch (e.type){
    					case SWT.Selection:
					
    						new Thread() {
    							public void run(){
    								setEnable(false); 	
		            
    								display.asyncExec(new Runnable () {
    									public void run() {
    										try{
    											//String atftp[]={"/bin/sh", "-c", "expect/ping.sh "+router3.getText()};
    											String atftp[]={"/bin/sh", "-c", "ping -c 5 192.168.2.214"};
    											Process d = Runtime.getRuntime().exec(atftp);
    											d.waitFor();
    											BufferedReader in = new BufferedReader( new InputStreamReader((d.getInputStream())));
    											int i=0;
    											for (String pingbuffer;(pingbuffer = in.readLine()) != null;)
    											{
    												control.add(pingbuffer);
    												if(pingbuffer.equals("Erfolgreich"))
    												{
    													check.add("Router ist erreichbar");
    													i++;
    												}
    											}
    											if(i==0)//Falls ein Fehler da ist, schreibe...
    											{
    												check.add("Fehler: Routers wurde nicht erreicht");
    											}
    											in.close();
						
    											BufferedReader is = new BufferedReader( new InputStreamReader((d.getErrorStream())));
    											for (String pingbuffer;(pingbuffer = is.readLine()) != null;control.add(pingbuffer));
    											is.close();
    											control.add("");
    										}
    										catch(IOException ioexp){
    											control.add("Fehler bei dem IOExecption");
    										}
    										catch(Exception exp){
    											control.add("Fehler bei dem Execption");
    										}
    									}
    								});
    								setEnable(true);
    							}
    						}.start();	
						}
					}
				});

Java:
	public static void setEnable(final boolean f){
		display.asyncExec(new Runnable () {
			public void run() {
        		ping.setEnabled(f);
        		exitbutton.setEnabled(f);
        		telnet.setEnabled(f);
        		java.setEnabled(f);
        		firmware.setEnabled(f);
            }
            });




Also ein Thread sieht dann so aus:

Java:
//Starten des Prozesses
new Thread() {
  public void run() {

    //Veränderung 1 GUI -> Button disablen
    display.asyncExec(new Runnable () {
      public void run() {
      //Mache die GUI Veränderung

      }
    });

    //Veränderung 2 GUI -> Schreibe Ping-ergebnis
    display.asyncExec(new Runnable () {
      public void run() {
      //Mache die GUI Veränderung

      }
    });

    //Veränderung 3 GUI -> Butten enablen
    display.asyncExec(new Runnable () {
      public void run() {
      //Mache die GUI Veränderung

      }
    });

  };
}.start();
 

Zurück
Oben