PDA

View Full Version : [Java] Swing - Problema aggiornamento pannello tramite Graphics2D


JavaError
08-02-2010, 15:35
Ciao a tutti,
Avrei bisogno di un consiglio per completare un piccolo programmino che sto facendo...

Il programma disegna una funzione con dei pallini al posto della tracciatura del grafico, ho fatto decine di prove, ma evidentemente non ho capito bene come si faccia a disegnare "progressivamente" su di un panel... l'applicazione in se funziona e fa quello che deve, solo che non è completa, e dovrei completarla facendo appunto fare la progressione del disegno di tale funzione sul pannello invece me la disegna o tutta ( tutta in blocco) oppure son riuscito a disegnare solo un pallino...

Inoltre la progressione mi è venuto in mente che potrebbe essere "rallentata" attraverso un thread, tuttavia son riuscito a rallentare al massimo l'aggiornamento dello coordinate e non il disegno in se...

So che sto sbagliando il "modo" in cui richiamo il paintComponent(Graphics g)..

però mi sembra che il metodo update() non abbia effetto...

Insomma, mi son un pò impantanato.. qualche consiglio ???

Ho trovato esempi in giro, tuttavia riguardano al 90% applet e purtroppo io non posso lavorare con le applet ma Devo per forza risolvere questo "mistero"...

Grazie in anticipo.

import java.awt.*;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.*; // necessario per le linee 2D
import javax.swing.*;
import javax.swing.event.*; // necessario per changelistener
import java.util.Random;

class EsercizioTrigonometrico extends JFrame {
private Pannello panTrigo;
private JSlider ctrCicli;
private JButton crea;
private JButton cancella;
private Random rand; // per il numero random

public EsercizioTrigonometrico() {
Container cnt = getContentPane();
panTrigo = new Pannello();
rand = new Random();
crea = new JButton("Draw");
cancella = new JButton("Cancel");
panTrigo.setBounds (0,0,400,400);
panTrigo.creaCordinate();
cnt.add(BorderLayout.CENTER, panTrigo);
cnt.add(BorderLayout.EAST, cancella);
cnt.add(BorderLayout.WEST, crea);
}
// Metodo che genera un colore random
public Color randomColor(){
return(new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255))); // l'ultimo serve per la trasparenza del colore
}
// pannello della funzione trigonometrica
class Pannello extends JPanel {
private int DensPUNTI = 60; // densità dei punti 30
private static final int nrCicli =3; // numero dei cicli
private int nrPunti = 360; // numero dei punti 180
private static final int NrBOLLExCICLO = 30;
private double[] cordinateDouble = new double[nrPunti];// array di double di 180 posizioni ?
private int[] cordinateInt;
// altre dichiarazioni
private int numeroBolle = nrPunti / (NrBOLLExCICLO*nrCicli); // 180 punti diviso 180 bolle
private int maxHeight = 400;
private int maxWidth = 400;
private double h = (double) maxWidth/(double)nrPunti; // Determinazione del passo di campionamento

public Pannello() {
}

public void creaCordinate() {
for(int i = 0; i < nrPunti; i++) {
double rad = (Math.PI / DensPUNTI) * i;
cordinateDouble[i] = Math.sin(rad);
}
// Determinazione delle coordinate intere di visualizzazione
cordinateInt = new int[nrPunti];
for(int i = 0; i < nrPunti; i++) {
cordinateInt[i] = (int)(cordinateDouble[i] * maxHeight/2 * .95+maxHeight/2);
}
}
// uso di paintComponent()
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i < nrPunti; i++) { // Tracciatura del grafico nrPunti
int x1 = (int) ((i)*h);
int y1 = cordinateInt[i];
if(i % numeroBolle == 0) {
g2.setColor(randomColor());
g2.fill(new Ellipse2D.Double(x1,y1, 30, 30));

/* try{
Thread.sleep(100);
repaint();
System.out.println(i);
}catch(InterruptedException e) {}*/
}

}
}

}
public static void main(String[] args) {
JFrame trigonometria = new EsercizioTrigonometrico();
trigonometria.setBounds(100,100,700,500);
trigonometria.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
trigonometria.setVisible(true);
}

}



Dimenticavo...
Sistema Operativo - Ubuntu
Compilatore - Geany 0.16

JavaError
08-02-2010, 17:32
Ho creato un metodo apposta per suddividere il lavoro, tuttavia rimane il fatto che crea effettivamente le bolle ( con System.out.prinltln) lo controllo...
Però non riesco a disegnarle sul pannello!


//metodo che dovrebbe mandare in creazione tutte le bolle una dietro l'altra // e lo fa!!! ma viene disegnata solo l'ultima bolla!
public void disegnaBolle() {
for(int i = 0; i < 360; i++) { // Tracciatura del grafico 360 punti
int x1 = (int) ((i)*h);
int y1 = cordinateInt[i];
if(i % numeroBolle == 0) {
disegnaUnaBolla(x1, y1, 30, randomColor());
System.out.println(bolla.getX()+" "+bolla.getY());
}
}
}
// metodo che dovrebbe disegnare una unica bolla
public void disegnaUnaBolla(int x1, int y1, int r, Color c) {
bolla = new Bolla(x1,y1,r,c);
System.out.println("Hai creato una bolla"); // verifica che venga creata
repaint();
}
// metodo che dovrebbe servire a mantenere i dati presenti in graphics ( g )
public void update(Graphics g) {
paint(g);
}
// uso di paintComponent() - Disegna 1 Bolla con i valorei passati
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if(bolla != null) { // disegna l'interfaccia anche se non ci sono bolle
g2.setColor(bolla.getColore());
g2.fill(new Ellipse2D.Double(bolla.getX(),bolla.getY(),bolla.getR(),bolla.getR()));
}
}

insomma.. crea la bolla, gli da le coordinate, e "dovrebbe" andare a disegnarsi, invece, mi crea tutte le bolle, e mi disegna solo l'ultima...

idee... ? ( anche vaghe!)

brancolo nel buio..:mc:

PGI-Bis
08-02-2010, 19:19
Il problema ha molte soluzioni, abbastanza per poter affermare: "puoi fare come preferisci". Si tratta semplicemente di scegliere l'astrazione che più ti convince. Esempio.

Supponiamo di avere un componente P che estende JPanel e ha una lista L di oggetti "Paintable".

Un oggetto "Paintable" è tale se possiede un metodo paint(Graphics g).

Il metodo paintComponent di P dice:

per ogni Paintable x in L, x.paint(g)

Questo pannello "P" ha un metodo, "addPaintable(Paintable p)" che:

aggiunge p a L
invoca repaint

Fine.

Il disegno di P è cumulativo nel senso che puoi passargli un nuovo punto ogni 3 secondi, quel punto va nella lista, e l'invocazione di repaint causa il disegno di tutti i punti finora aggiunti a P.

Per temporizzare il disegno puoi generare una pila di punti e poi passare quella pila a un Timer (javax.swing.Timer) che ogni X millisecondi estrae un punto e lo passa a P.

JavaError
09-02-2010, 08:34
Capisco quello che mi hai postato, ma cercherò di essere più preciso..

Allora... io ho creato una classe che crea una elemento Bolla che contiene i parametri di un Ellipse2D... tale oggetto bolla, viene poi creato in modo sequenziale in base alle coordinate che un altro metodo mi crea per il disegno del grafico sul JPanel...

Per disegnare il tutto insieme, c'ero già riuscito come dicevo all'inizio ora...dovrei ottenere un animazione incrementale del processo di disegno...

Ora visto che le bolle ci sono, le coordinate pure, il passaggio di parametri pure, mi chiedo perché il metodo paintComponent(Graphics g) mi disegna solamente l'ultima bolla ?? praticamente il repaint() è come se non lo sentisse infatti viene invocato 2 volte...

ho notato che: super.paintComponent(g); basandomi su altri esercizi dovrebbe servire a ripulire il pannello prima di disegnare altro e quindi non dovrei usarlo infatti per ora l'ho messo tra \\....

Ora mi domando come posso conservare il disegno effettuato sul pannello tramite paintComponent(Graphics g) ??

Se sposto tutto il processo di disegno all'interno del paintComponent ottengo il risultato che vedi in figura...

il risultato deve essere il medesimo, tuttavia utilizzando un Thread e non un timer dovrei vedere lo sviluppo.. sinceramente il thread non dovrei avere problemi ad implementarlo invece il fatto di disegnare 2 bolle una consecutiva all'altra senza che venga resettato il pannello quello per ora ( anche se mi sembra strano che in altri esercizi ( applet ) mi venga) non riesco a farlo...

Se puoi/vuoi sai dove trovare un esempio che mi disegni tramite paintComponent 2 ellissi sullo stesso panello..

Io sto cercando ma non trovo niente..

da notare che invece.. con il metodo public void paint (Graphics g) {
sto facendo delle prove e non mi sembra che ci sian gli stessi problemi..

:mbe:

PGI-Bis
09-02-2010, 16:10
Una soluzione è quella che ti ho già proposto. Posso farti un esempio:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.LinkedList;
import java.util.Random;

public class Main {

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Main().execute();
}
});
}

private void execute() {
final int WIDTH = 500;
final int HEIGHT = 500;
final long DURATIONMS = 10000;
final int RESOLUTIONMS = 25;
final PCanvas CANVAS = new PCanvas();
final Parametric PARAMETRIC = new Sinusoid(CANVAS, WIDTH, HEIGHT);
final Generator GENERATOR = new Generator(PARAMETRIC, DURATIONMS, RESOLUTIONMS);
final JFrame WINDOW = new JFrame("Test");
final JButton START = new JButton(new AbstractAction("Start") {
public void actionPerformed(ActionEvent e) {
GENERATOR.start();
}
});

CANVAS.getComponent().setPreferredSize(new Dimension(WIDTH, HEIGHT));
CANVAS.getComponent().setBackground(Color.WHITE);
WINDOW.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
WINDOW.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
GENERATOR.stop();
}
});
WINDOW.add(CANVAS.getComponent());
WINDOW.add(START, BorderLayout.SOUTH);
WINDOW.pack();
WINDOW.setVisible(true);
}
}

class Sinusoid implements Parametric {
private final Random RANDOM = new Random(0);
private final PCanvas CANVAS;
private final int WIDTH;
private final int HEIGHT;

public Sinusoid(PCanvas canvas, int width, int height) {
CANVAS = canvas;
WIDTH = width;
HEIGHT = height;
}

public void apply(Double t) {
final double Y = HEIGHT / 2 * Math.sin(t * 2 * Math.PI) + HEIGHT / 2;
final double X = WIDTH * t; //(0 ~ WIDTH)
final double RAY = 5;
final Color FILL = new Color(RANDOM.nextInt(255), RANDOM.nextInt(255), RANDOM.nextInt(255));
final Bubble BUBBLE = new Bubble(X, Y, RAY, FILL);
CANVAS.push(BUBBLE);
}
}

class Generator {
private final TimeLine TIMELINE = new TimeLine();
private final Parametric FUNCTION;
private final javax.swing.Timer TIMER;
private final long DURATION;

public Generator(Parametric fun, long durationMs, int resolutionMs) {
FUNCTION = fun;
DURATION = durationMs;
TIMER = new javax.swing.Timer(resolutionMs, new ActionListener() {
public void actionPerformed(ActionEvent e) {
applyParametric();
}
});
}

private void applyParametric() {
double t = (double)TIMELINE.get() / (double) DURATION;
TIMELINE.update();
if(t >= 1) {
t = 1;
FUNCTION.apply(t);
stop();
} else {
FUNCTION.apply(t);
}
}

public void start() {
TIMELINE.start();
TIMER.restart();
}

public void stop() {
TIMER.stop();
}
}

class TimeLine {
private final long MILLION = 1000000;
private long then;
private long time;

public void start() {
then = System.nanoTime();
time = 0;
}

public void update() {
long now = System.nanoTime();
long dTimeNanos = now - then;
then = now;
time += dTimeNanos / MILLION;
}

public long get() {
return time;
}
}

interface Parametric {

public void apply(Double t);
}

interface Paintable {

void paint(Graphics g);
}

class PCanvas {
private final LinkedList<Paintable> PAINTABLES = new LinkedList<Paintable>();

private final JPanel PANEL = new JPanel() {
@Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
for(Paintable p : PAINTABLES) {
p.paint(graphics);
}
}
};

public void push(Paintable p) {
PAINTABLES.push(p);
PANEL.repaint();
}

public void clear() {
PAINTABLES.clear();
PANEL.repaint();
}

public Component getComponent() {
return PANEL;
}
}

class Bubble implements Paintable {
private final Color FILL;
private final Shape SHAPE;

public Bubble(double x, double y, double ray, Color fill) {
FILL = fill;
SHAPE = new Ellipse2D.Double(x - ray, y - ray, 2 * ray, 2 * ray);
}

public void paint(Graphics g) {
Graphics2D graphics = (Graphics2D) g;
graphics.setPaint(FILL);
graphics.fill(SHAPE);
}
}

Comunque il problema del tuo codice è che esprime un'idea di disegno che presuppone un Graphics "stateful" mentre Graphics è "stateless". Detto altrimenti lo stato di Graphics viene azzerato tra due invocazioni di repaint successive. Così se disegni una bolla in 10, 10 e la volta seguente disegni una bolla in 20, 20 a video appare una sola bolla, in 20,20. Se vuoi farle apparire tutte e due la prima volta disegni la bolla (10,10), la seconda volta disegni la bolla (10,10) e la bolla (20,20).

JavaError
19-02-2010, 17:41
ok, ho risolto:D , anche se non brillantemente come nel tuo esempio..:O

alla prox.