Innerhalb eines Fensters ein neues erschaffen

Shadoka

Mitglied
Hi,

gleich vorneweg: Ich bin hier neu und hoffe, dass dies hier das richtige Unterforum ist.

Also...ich habe ein Programm geschrieben, welches eine Textdatei kopiert, zusammen mit einem zugehörigen GUI. Nichts besonderes, aber ich hätte gerne, dass der Ladevorgang in einem Extrafenster angezeigt wird. Der zugehörige Ladebalken wäre nicht das Problem, aber ich frage mich, wie ich, wenn ich auf einen Button drücke, ein neues Fenster öffne.

Ich hoffe, dass mein Problem verständlich ist.

Falls benötigt, hier der Code.

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

class Copymakers
{
  String sourcename, destname;
  BufferedReader source;
  PrintWriter dest;
  String line;

  Copymakers(String source, String dest)
  {
    sourcename = source;
    destname = dest;
  }

  public boolean openSourceFile()
  {
    try
    {
      source = new BufferedReader(new FileReader(sourcename));
      return true;
    }
    catch (IOException iox)
    {
      System.out.println("Problem opening " + sourcename);
      return false;
    }
  }

  public boolean openDestinationFile()
  {
    try
    {
      dest = new PrintWriter(new BufferedWriter(new FileWriter(destname)));
      return true;
    }
    catch (IOException iox)
    {
      System.out.println("Problem opening " + destname);
      return false;
    }
  }

  public void closeFiles()
  {
    try
    {
      source.close();
    }
    catch (IOException iox)
    {
      System.out.println("Problem closing " + sourcename);
    }

    dest.close();
  }

  public boolean copyFiles()
  {
    try
    {
      while((line = source.readLine()) != null)
      {
        dest.println(line);
      }
      return true;
    }
    catch (IOException iox)
    {
      System.out.println("Problem reading or writing");
      return false;
    }
  }
}

public class copyprogramm extends JFrame implements ActionListener
{
  String sources, dests;
  
  JLabel src = new JLabel("Enter the name of the sourcefile(10 chars, .txt)");
  JLabel dst = new JLabel("Enter the name of the destinationfile(10 chars, .txt)");
  JTextField result = new JTextField("Give me some work");
  JTextField insrc = new JTextField(14);
  JTextField indst = new JTextField(14);
  JButton push;
  
  JPanel srcp = new JPanel();
  JPanel dstp = new JPanel();
  JPanel rstp = new JPanel();
  
  public copyprogramm()
  {
    push = new JButton("Lets do it");
    setTitle("Copyprogramm");
    result.setEditable(false);
    
    srcp.add(src);
    srcp.add(insrc);
    dstp.add(dst);
    dstp.add(indst);
    rstp.add(result);
    rstp.add(push);
    
    getContentPane().add(srcp);
    getContentPane().add(dstp);
    getContentPane().add(rstp);
    
    push.addActionListener(this);
    
    getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
  }
  
  public void actionPerformed(ActionEvent ex)
  {
    String inData;
    inData = insrc.getText();
    sources = inData;
    
    inData = indst.getText();
    dests = inData;
    
    Copymakers cpm = new Copymakers(sources, dests);
    if (cpm.openSourceFile())
    {
      if (cpm.openDestinationFile())
      {
        if (cpm.copyFiles())
        {
          result.setText("Done");
        }
        else
          result.setText("Problem at copying Files");
      }
      else
        result.setText("Problem with Destination");
    }
    else
      result.setText("Problem with source");

    cpm.closeFiles();
  }
  
  public static void main(String[] args)
  {
    copyprogramm frame = new copyprogramm();

    windowquit wquit = new windowquit();
    frame.addWindowListener(wquit);

    frame.setSize(640, 480);
    frame.setVisible(true);
  }
}

class windowquit extends WindowAdapter
{
  public void WindowClosing(WindowEvent e)
  {
    System.exit(0);
  }
}
 
So wie das andere Fenster auch.

Java:
JFrame frame = new JFrame();

bar = new JProgressBar(); //Klassenvariable für externen Zugriff.
frame.add(bar, BorderLayout.CENTER);

frame.pack();
frame.setLocationRelativeTo(null); //Oder anderes Frame.
frame.setVisible(true);

Du musst das Kopieren aber in einen anderen Thread auslagern, sonst kann eine Fensteränderung wärend dessen nicht gezeichnet werden.

btw, Klassennamen wenn möglich immer groß schreiben, und für ein JFrame (im Gegensatz zu einen Frame) brauchst du zum Beenden keinen WindowListener mehr.

Java:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Beendet das Programm.
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); //"Entsorgt" das Fenster.
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); //Macht das Fenster unsichtbar.
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); //Macht garnichts, ideal für Listener.

edit: Ach ja, ein JDialog wäre dafür vermutlich besser geeignet.
 
Zuletzt bearbeitet:
Danke schonmal für die schnelle Antwort.

Das ich ein neues Frame erschaffen muss habe ich mir schon gedacht, ich weiß nur nicht, wo das innerhalb des Programmcodes geschehen soll. Das Fenster soll ja erscheinen, wenn das Event am JButton push ausgelöst wird...gehört das neue Frame dann auch in actionPerformed?

Und auch das mit dem Thread ist mir nicht ganz klar, ich hatte bisher nichts damit zu tun, gehört das zwangsläufig dazu?

€: Ich benutze glaube ich ein etwas veraltetes Tutorial, vielleicht kommts ja auch daher.
 
Das ich ein neues Frame erschaffen muss habe ich mir schon gedacht, ich weiß nur nicht, wo das innerhalb des Programmcodes geschehen soll. Das Fenster soll ja erscheinen, wenn das Event am JButton push ausgelöst wird...gehört das neue Frame dann auch in actionPerformed?
Jein. Theoretisch solltest du es nicht tun, denn...

Und auch das mit dem Thread ist mir nicht ganz klar, ich hatte bisher nichts damit zu tun, gehört das zwangsläufig dazu?
...Swing besitzt einen eigenen Thread zum Verarbeiten der Events aus der Oberfläche und deren Zeichnung. Dieser sogenannte Event Dispatch Thread ist also daüfr verantwortlich, deine Oberfläche zu zeichnen, und dass bedeutet, dass langlaufende Operationen, die den Thread auslasten, zugleich für die Dauer ihrer Laufzeit deine GUI einfrieren. Das kannst du machen, ist aber hässlich.
Aus diesem Grunde sollte man EDT und (langelaufende) Aktionen trennen, indem man die Aktionen in einem eigenen Thread ausführt und alle Zeichenoperationen (bzw. alle Operationen, die sich auf die GUI auswirken) im EDT.
Ein Beispiel wäre:
Java:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Foo extends JFrame implements ActionListener {

	private JButton btnDoSomething;

	public Foo() {
		super("Foo");
		btnDoSomething = new JButton("Klick mich!");
		btnDoSomething.addActionListener(this);
		this.add(btnDoSomething);
		this.pack();
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		Runnable longRunningTask = new Runnable() {

			@Override
			public void run() {
				// Fortschrittsdialog vorbereiten:
				final JProgressBar progress = new JProgressBar(0, 100);
				final JDialog progressDialog = new JDialog(Foo.this, "Langer Task...", true);
				Runnable progressRunnable = new Runnable() {

					@Override
					public void run() {
						progressDialog.add(progress);
						progressDialog.pack();
						progressDialog.setLocationRelativeTo(Foo.this);
						progressDialog.setVisible(true);
					}
				};
				SwingUtilities.invokeLater(progressRunnable);
				// Es folgt die lange Aufgabe:
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(1000);
					} catch (Exception e) {
						e.printStackTrace();
					}
					// Fortschritt darstellen:
					SwingUtilities.invokeLater(new Runnable() {

						@Override
						public void run() {
							progress.setValue(progress.getValue() + 10);
						}
					});
				}
				SwingUtilities.invokeLater(new Runnable() {

						@Override
						public void run() {
							progressDialog.setVisible(false);
							progressDialog.dispose();
						}
					});
			}
		};
		new Thread(longRunningTask).start();
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				Foo foo = new Foo();
				foo.setLocationRelativeTo(null);
				foo.setVisible(true);
			}
		});
	}
}
Dieses Beispiel ist jetzt sicherlich nicht das beste, sollte aber das grundlegende Prinzip aufzeigen: Lange Operationen laufen in einem eigenen Thread, alle Zeichenoperationen werden über [c]SwingUtilities.invokeLater(Runnable)[/c] an den EDT übergeben.
 
Ok, danke für das ausführliche Beispiel.
Dann werde ich mir wohl zunächst einmal die Wirkungsweise von Threads, obwohl ich die schon grob verstanden habe, nochmal genauer anschauen.

In deinem Beispiel gibt es allerdings auch ein paar Sachen, die ich absolut nicht kenne, zum Beispiel die Klasse "Runnable" und das "@Override", wobei ich mir das eventuell mit dem Überschreiben einer Klasse oder Methode erklären könnte.

Wäre nett, wenn du dazu noch was sagen könntest 🙂
 
Runnable ist keine Klasse, sondern ein Interface. Das definiert einfach eine oder mehrere Methodensignaturen, wodurch andere Klassen (hier Thread oder SwingUtilities) sichergehen kann, dass eine Methode existiert.
"@Override" ist eine sogenannte annotation, also Anmerkung. Davon gibt es ein paar (wie z. B. "@Deprecated"), die können ober einer Methode oder der Verwendung einer Klasse/Methode innerhalber einer Methode stehen. Dabei gibt z. B. Override an, dass die Methode wirklich überschrieben werden soll, und nicht versehendlich so genannt wird. Deprecated, gibt an, dass die jeweilige Methode veraltert ist. SuppressWarnings("deprecation"), das dir bewusst ist, das eine Methode, die du verwendest, als veraltet gekennzeichnet ist usw.
 
Der Vorteil von @Override ist, dass Tipp- und Logikfehler ausgeschlossen werden. Der Compiler prüft, ob die Methode mit der Annotation tatsächlich eine bestehende Methode überschreibt und bricht bei negativem Ergebnis den Kompiliervorgang ab. Nützlich bei so bösen Fehlern wie [c]public boolean equals(Foo o)[/c] statt [c]public boolean equals(Object o)[/c].
Annotation stehen ab Java 1.5 zur Verfügung.
 

Zurück
Oben