# Problem mit dem Zeichnen einer Linie auf einem JPanel



## Stulle (11. Sep 2006)

Hallo!
Ich habe folgendes Problem:
Ich habe einen JPanel auf einem JFrame erzeugt. Darauf wollte ich folgender Weise eine gruene Linie zeichnen:

...
jPanel1.getGraphics().setColor(Color.green);
jPanel1.getGraphics().drawLine(0,0,100,100);
...

Nun zeichnet er zwar die Linie, aber nicht in gruen sondern standardmaessig in schwarz.

Warum geht das nicht?

Vielen Danke im vorraus.


----------



## SlaterB (11. Sep 2006)

Zwar tricky, aber auch nicht allzu tricky:
beim Aufruf von getGraphics() wird alles auf Standard gesetzt,
also auch die Farbe (zurück) auf Schwarz.

->

graphics g = getGraphics()
g.setSchwarz()
g.drawLinie()

--------

Zeichnen sollte man doch eh nur in der paint-Operation, und da wird das Graphics-Objekt doch als Parameter geliefert? 
Wann rufst du denn überhaupt getGraphics auf?


----------



## Stulle (11. Sep 2006)

Dann ist doch die setColor Methode schwachsinnig, wenn man mit getGraphics immer wieder zuruecksetzt.
Oder nicht?


----------



## The_S (11. Sep 2006)

Nö, wird ja immer wieder neu gezeichnet, von daher musst du immer wieder die Farbe setzen  .


----------



## Stulle (11. Sep 2006)

Geht das denn ueberhaupt auf diese Weise irgendwie?
Ich meine wenn erst zeichne, dann die Farbe setze und dann ueber jPanel1.repaint() den Bereich neuzeichnen lasse, macht er es auch nicht.


----------



## Leroy42 (11. Sep 2006)

Stulle hat gesagt.:
			
		

> Ich meine wenn erst zeichne, dann die Farbe setze und dann ueber jPanel1.repaint() den Bereich neuzeichnen lasse, macht er es auch nicht.



repaint() initiiert ein Aufruf von paint() das ein _frisches_ Graphics mitbekommt.

Deshalb, wie schon gesagt, Zeichnungen nur in der paint/paintComponent-Methode durchführen.


----------



## SlaterB (11. Sep 2006)

und außerdem habe ich doch bereits gepostet:

... 
jPanel1.getGraphics().setColor(Color.green); 
jPanel1.getGraphics().drawLine(0,0,100,100); 
... 

ersetzen durch

... 
Graphics g = jPanel1.getGraphics();
g.setColor(Color.green); 
g.drawLine(0,0,100,100); 
... 

dann klappts auch mit der Farbe..


----------



## Leroy42 (11. Sep 2006)

Ich schätze mal, daß Stulle die Farbe von _außerhalb_ setzten will.



			
				Stulle hat gesagt.:
			
		

> Ich meine wenn erst zeichne, dann die Farbe setze und dann ueber jPanel1.repaint() den Bereich neuzeichnen lasse, macht er es auch nicht.



Wobei mir nicht klars ist, was er mit _erst zeichnen, dann die Farbe setzen_ meint.  ???:L


----------



## Stulle (11. Sep 2006)

Okay, ihr muesst mich mittlerweile fuer total bescheuert halten. Aber....

Fuer die paint Methode muss ich nen Graphics uebergeben. Habe mir also ne neue Variable vom Typ Graphics mit Namen help gemacht. Da habe ich dann die Farbe festgelegt. 
Wie bekomme ich jetzt die Linie auf den Panel? Damit geht es ja schon mal nicht:

jPanel1.paint(help.drawLine(0,0,100,100)):

und damit auch nicht:

help.drawLine(0,0,100,100);
jPanel1.paint(help);

Also wie soll das gehen?

Ich weiss hier werden keine Hausaufgaben fuer einen gemacht und so. Aber ich brauche dringend Hilfe. Ist auch keine Hausaufgabe.


----------



## Leroy42 (11. Sep 2006)

Stulle hat gesagt.:
			
		

> Fuer die paint Methode muss ich nen Graphics uebergeben.



Du mußt der paint-Methode kein Graphics übergeben, da du diese Methode
_selbst_ überhaupt nicht aufrufst.  :noe: 

Du rufst nur repaint() auf und initiierst damit einen Aufruf von paint(),
mit korrektem Graphics-Parameter, durch das System.

Alle deine Zeichnungen erfolgen _ausschließlich_ in der, von dir
überlagerten, paint()/paintComponent()-Methode; also auch das Setzen
der Farbe.


----------



## Stulle (11. Sep 2006)

Meinst du etwa so?

    private void XBahnbuttonMouseClicked(java.awt.event.MouseEvent evt) {                                         

       help.setColor(Color.green);
       help.drawLine(KoorX1,KoorY1,KoorX2,KoorY2);
       jPanel1.repaint();

    }                                        

Nicht wirklich, oder?


----------



## Leroy42 (11. Sep 2006)

Stulle hat gesagt.:
			
		

> Nicht wirklich, oder?



 Nein, wirklich nicht.

Eher so


```
boolean buttonPressed = false;

public void paintComponent(Graphics g) { // paintComponent von jpanel1 überschreiben
  super.paintComponent(g);
  if (buttonPressed) {
    g.setColor(Color.green); 
    g.drawLine(KoorX1,KoorY1,KoorX2,KoorY2);
  } else {
  }
}

private void XBahnbuttonMouseClicked(java.awt.event.MouseEvent evt) { 
  buttonPressed = true;
  jPanel1.repaint();
}
```


----------



## Stulle (11. Sep 2006)

Bin ich wirklich so doof, dass ich das nicht verstehe?

Wenn ich das so mache passiert da nichts.
Muss ich dann nicht noch jPanel1.paintComponents(help) aufrufen, damit er das macht?

Habe ich ausprobiert. Dann bekomme ich aber nach dem Ausfuehren viele wunderschoene Fehlermeldungen raus.


----------



## Leroy42 (11. Sep 2006)

Stulle hat gesagt.:
			
		

> Bin ich wirklich so doof, dass ich das nicht verstehe?


Nein, nur noch nicht mit dem Grundsätzlichen des Handlings von GUIs vertraut.

Poste doch mal die Codeausschnitte

- Die überschriebene paintComponent-Methode (Wie geschrieben, die von jpanel1 muß überlagert werden)
- Den Teil wo der ActionListener deines Buttons ge_addet_ wird.
- Die Aktion des ActionListeners


----------



## Leroy42 (11. Sep 2006)

Stulle hat gesagt.:
			
		

> Muss ich dann nicht noch jPanel1.paintComponents(help) aufrufen, damit er das macht?



Im Übrigen dürfte dein Code gar kein _help_ mehr enthalten.


----------



## Stulle (11. Sep 2006)

Hier einmal die ueberlagerte Methode:


```
public void paintComponent(Graphics g) { // paintComponent von jpanel1 überschreiben
super.paintComponents(g);
  if (buttonPressed) {
    g.setColor(Color.green);
    g.drawLine(KoorX1,KoorY1,KoorX2,KoorY2);
  } else {
  } 
} 

[\code]
Habe ich halt so uebernommen.


Den Actionlistener habe ich nicht selber geaddet, sondern wurde von Netbeans  automatisch erstellet. Ich benutze den GUIBuilder von Netbeans. Vielleicht kannst du damit was anfangen:

[code]
        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jPanel1Layout.createSequentialGroup()
                        .add(50, 50, 50)
                        .add(jRadioButton1))
                    .add(jPanel1Layout.createSequentialGroup()
                        .add(151, 151, 151)
                        .add(jRadioButton2)))
                .addContainerGap(1109, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .add(52, 52, 52)
                .add(jRadioButton1)
                .add(92, 92, 92)
                .add(jRadioButton2)
                .addContainerGap(178, Short.MAX_VALUE))
        );
[\code]

Und die Aktion habe ich auch von dir uebernommen:

[code]
    private void XBahnbuttonMouseClicked(java.awt.event.MouseEvent evt) {                                         
       jRadioButton1.setSelected(false);
       jRadioButton2.setSelected(false);
       buttonPressed = true;
       jPanel1.repaint();
    }           

[\code]
```


----------



## Leroy42 (11. Sep 2006)

Ist jPanel1 überhaupt eine Instanz einer von JPanel *erweiterten* Klasse?  :shock: 

Nur in diese erweiterte Klasse kannst du paintComponent überlagern:


```
class MyPanel extends JPanel {
  public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    if (buttonPressed) { 
      g.setColor(Color.green); 
      g.drawLine(KoorX1,KoorY1,KoorX2,KoorY2); 
    } else { 
    } 
  }
}
...
MyPanel jpanel1 = new MyPanel();
```
oder wo hast du die paintComponent-Methode hingeschrieben?  ???:L


----------



## SlaterB (11. Sep 2006)

Bei dem Diskussionsstand kann das ja noch ewig weitergehen.
Vielleicht sorgt da mal ein funktionierendes Beispiel für Übersicht? 


```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TestFrame extends JFrame {

    private MyPanel p = new MyPanel();

    public TestFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton b = new JButton("test");
        b.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                System.out.println("Button");
                p.setColor(Color.GREEN);
                p.setDrawLine1(Math.random() > 0.5);
                p.setDrawLine2(Math.random() > 0.5);
                repaint();
            }

        });
        getContentPane().add(b, BorderLayout.NORTH);
        getContentPane().add(p, BorderLayout.CENTER);

        setSize(400, 400);
        setVisible(true);
    }

    public static void main(String[] args) {
        new TestFrame();

    }

    class MyPanel extends JPanel {
        private Color color;

        private boolean drawLine1;

        private boolean drawLine2;

        public void paint(Graphics g) {
            super.paint(g);
            g.setColor(Color.RED);
            if (color != null) {
                g.setColor(color);
            }
            g.drawLine(5, 5, 200, 200);
            if (drawLine1) {
                g.drawLine(20, 100, 400, 100);
            }
            if (drawLine2) {
                g.drawLine(70, 10, 200, 300);
            }

        }

        public Color getColor() {
            return color;
        }

        public void setColor(Color color) {
            this.color = color;
        }

        public boolean isDrawLine1() {
            return drawLine1;
        }

        public void setDrawLine1(boolean drawLine1) {
            this.drawLine1 = drawLine1;
        }

        public boolean isDrawLine2() {
            return drawLine2;
        }

        public void setDrawLine2(boolean drawLine2) {
            this.drawLine2 = drawLine2;
        }

    };
}
```

Wichtig ist die Philosphie, dass nur das JPanel selber zeichnet.
Von außen sollten nur Hinweise gegeben werden wie 
'zeichne doch bitte eine bestimmte Linie beim nächsten repaint'.

Wenn man natürlich beliebige 4 Punkte festlegen möchte und beliebige viele Linien oder auch noch andere Punkte, 
dann kommt man mit so einer Trennung natürlich nicht weit und muss sich was anderes überlegen.
Aber es bleibt technisch dabei, dass man nicht von außen drawIrgendwas() aufrufen kann.


----------



## Leroy42 (11. Sep 2006)

SlaterB hat gesagt.:
			
		

> Bei dem Diskussionsstand kann das ja noch ewig weitergehen.
> Vielleicht sorgt da mal ein funktionierendes Beispiel für Übersicht?



Da muß ich dir Recht geben!   




			
				SlaterB hat gesagt.:
			
		

> Aber es bleibt technisch dabei, dass man nicht von außen drawIrgendwas() aufrufen kann.





			
				Pingeliger hat gesagt.:
			
		

> Aber es bleibt technisch dabei, dass man nicht von außen drawIrgendwas() aufrufen _sollte_.


----------



## Stulle (11. Sep 2006)

Vielen Dank euch schon mal für Hilfe. Werde mich morgen noch mal mit euren Beiträgen genauer damit befassen.
Hoffe ich steige entlich dahinter.


----------



## Eiwiw (12. Sep 2006)

Danke, dieser Thread hat mir ebenfalls ein Stück weitergeholfen, auch wenn mein Problem ein anderes war.


----------



## Stulle (13. Sep 2006)

Okay. Das habe ich jetzt soweit verstanden....    Leider!
Sehe ich das richtig, dass repaint dafür sorgt, dass alles neu gezeinet wird?
Man kann damit also nicht mehrer verschieden farbige Linien zusammen darstellen, richtig?


----------



## SlaterB (13. Sep 2006)

doch, du kannst doch zwischen den einzelnen draw-Kommandos die Farbe wechseln,
was aber nicht geht, ist zu einem Zeitpunkt x Objekt a zu malen, 3 Sekunden später Objekt b usw.

bei einem repaint wird immer alles neu gezeichnet,

---------

was du vielleicht eher benötigst ist ein Image-Objekt,
das kannst du beliebig bemalen lassen (wieder mit einem Graphics-Objekt), 
durch verschiedene Klassen reichen, 
auf der Festpaltte speichern und laden,
usw.

und letztendlich dem JPanel geben, dessen repaint dann aus 'zeiche Bild x' besteht

besser?


----------



## Stulle (13. Sep 2006)

Nee, das ist nicht so gut. Aber ist nicht schlimm. Da ich nur 3 Farben bauche, lege ich einfach 3 Panels übereinander, wo bei die beiden obersten transparent sind. Dann kann ich die verschieden Farben auf den drei verschiedenen Panel zeichnen und es sieht aus als wäre es eine Oberfläche. Ist wohl nicht schön, sollte aber gehen.


----------



## Stulle (13. Sep 2006)

Jetzt verstehe ich was du meinst mit verschiedene Linien zu verschiedenen Zeitpunkten. Das natürlich mist.
Ich schau mir das mal mit dem Image-Objekt an.


----------



## Leroy42 (13. Sep 2006)

SlaterB hat gesagt.:
			
		

> was aber nicht geht, ist zu einem Zeitpunkt x Objekt a zu malen, 3 Sekunden später Objekt b usw.



Nun entmutige Stulle doch nicht; natürlich geht das!   


```
import javax.swing.*;
import java.awt.*;
import java.util.*;

public class DrawTest extends JFrame {
	MyPanel panel = new MyPanel();
	public DrawTest() {
		setBounds(300, 300, 300, 300);
		getContentPane().add(panel);
		setVisible(true);
		new Thread(new Runnable() {
			public void run() {
				while (true) {
					++panel.lines;
					panel.repaint();
					try {Thread.sleep(250);} catch (InterruptedException e) {}
				}
			}
		}).start();
	}
	
	public static void main(String[] args) {
		new DrawTest();
	}
}

class MyPanel extends JPanel {
	static final Random rand = new Random();
	int lines;
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		if (lines==0) return;
		double h = getHeight() / lines;
		int w = getWidth();
		for (int i=0; i < lines; ++i) {
			g.setColor(new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255)));
			g.drawLine(0, (int) h*i, w, (int) h*i);
		}
	}
}
```

Das Programm zeichnet alle Viertel-Sekunde eine zusätzliche Linie.


----------



## Stulle (14. Sep 2006)

Okay, habe mir dein Programm vorgeknöpft und meines entsprechend geändert. Wenn ich das richtig sehe müsste ich die Koordinaten der Linien speichern und dann wenn eine hinzugekommen ist alle Linien in einer Schleife neu Zeichen, richtig? Jetzt zeichnet das Programm aber mal wieder garnichts. Woran liegt das?

Hier ist die Klasse des veränderten JPanels:


```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;


class MyPanel extends JPanel {
    
        int h = 0; 
        private int [] koors = new int [100];
        private Color color;
        private boolean drawLine1;
        private boolean drawLine2;
        private int koorX1=0;
        private int koorY1=0;
        private int koorX2=0;
        private int koorY2=0;
        private int lines=0;

        public void paint(Graphics g) {
            super.paint(g);
            if (drawLine1){
            if (lines==0) return;
            for (int i=1; i <= lines; ++i) {
                g.setColor(Color.yellow);
                g.drawLine(koors[i*4-4],koors[i*4-3],koors[i*4-2],koors[i*4-1]);
            }
            }
        }

        public Color getColor() {
            return color;
        }

        public void setColor(Color color) {
            this.color = color;
        }

        public boolean isDrawLine1() {
            return drawLine1;
        }

        public void setDrawLine1(boolean drawLine1) {
            this.drawLine1 = drawLine1;
        }

        public boolean isDrawLine2() {
            return drawLine2;
        }

        public void setDrawLine2(boolean drawLine2) {
            this.drawLine2 = drawLine2;
        }
        
        public void setKoor(int X1, int Y1, int X2, int Y2){
            koors [h] = X1;
            h++;
            koors [h]=Y1;
            h++;
            koors [h]=X2;
            h++;
            koors [h]=Y2;
            h++;
            lines++;
        }

    };
```

Und die entsprechende Hauptklasse:


```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Image;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TestFrame extends JFrame {

    private MyPanel p = new MyPanel();

    public TestFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton b = new JButton("test");
        b.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                p.setKoor(10,10,100,100);
                p.setDrawLine1(true);
                repaint();           
                p.setDrawLine1(false);
                p.setKoor(20,20,200,200);
                p.setDrawLine1(true);
                repaint();
            }

        });
        getContentPane().add(b, BorderLayout.NORTH);
        getContentPane().add(p, BorderLayout.CENTER);

        setSize(400, 400);
        setVisible(true);
    }

    public static void main(String[] args) {
        new TestFrame();

    }}
```

Kann sich das mal bitte jemand angucken? Ist zwar sehr schlecht geschrieben, aber ist ja auch erstmal nur zum Testen.


----------



## Leroy42 (14. Sep 2006)

Ich vermute der Fehler liegt in Zeile 29.

Schreibe

```
p.repaint();
```
anstelle von 

```
repaint();
```


----------



## Leroy42 (14. Sep 2006)

Noch etwas

```
public void actionPerformed(ActionEvent arg0) { 
                p.setKoor(10,10,100,100); 
                p.setDrawLine1(true); 
                repaint();            
                p.setDrawLine1(false); 
                p.setKoor(20,20,200,200); 
                p.setDrawLine1(true); 
                repaint(); 
            }
```

Was du vorhast, geht so nicht.
repaint() ist nur eine Anforderung an den *E*vent *D*ispatch *T*hread
Gezeichnet wird erst nach Beendigung der actionPerformed-Methode.

Wenn du mit einer Verzögerung verschiedenes zeichnen möchtest,
mußt du dies in einen separaten Thread auslagern.


```
public void actionPerformed(ActionEvent arg0) { 
  p.setKoor(10,10,100,100); 
  p.setDrawLine1(true); 
  p.repaint();            
  new Thread(new Runnable() {
    public void run() {
      // 2 Sekunden warten
      try {Thread.sleep(2000/*ms*/);} catch (InterruptedException e) {}
      p.setDrawLine1(false);   // ist hier unnötig
      p.setKoor(20,20,200,200); 
      p.setDrawLine1(true); 
      p.repaint(); 
  }}).start();
}
```

Vielleicht beschreibst du besser mal, was du eigentlich vorhast.  ???:L


----------



## Stulle (14. Sep 2006)

Okay, folgendes möchte ich machen:
Ich habe auf einem JPanel eine Menge von JRadioButtons verteilt. Nun soll der User solange er möchte immer zwei Knoten auswählen. Dort werde die Koordinaten ausgelesen und zwischen diesen Knoten eine z.B. gelbe Linie (Farbe muss nicht verändert werden) gezeichnet werden. Nachher soll das ganze aussehen wie eine Art Straßennetz.
Verstehst du was ich meine?


----------



## Stulle (14. Sep 2006)

JUHHHUUUUUUU!!!!!!!
Hier mit möchte ich die Diskusion schließen. Es fehlte wirklich nur noch p.repaint(); statt repaint(); Mit dem Koordinaten abspeichern und der Schleife um den kompletten Graphen neu zu zeichen funktioniert es. Endlich!

Vielen Dank noch mal euch allen für eure Mühen und eure schnelle und freundliche Hilfe, auch wenn ich nicht ganz einfach war. Wenn noch weitere Problem in diesem nervigen Uni Praktikum habe, weiß ich wo mich melden kann.

MFG 
Stulle


----------

