Designfrage bei rechenintensiver Operation

Status
Nicht offen für weitere Antworten.

jdk6man

Mitglied
Hallo leute,
Was macht ihr wenn euer Programm zur Darstellung des Fensters rechenintensive Vorgänge ausführen muss?
Ich hatte daran gedacht, die "Rechnerei" in einen Thread auszulagern und im main-Thread einen modalen Dialog mit einer Progressbar aufzurufen.
Wenn das in Frage kommt, gibt man dem Thread eigentlich die höchste Priorität oder nicht? Bei mir hat das jedenfalls dazu geführt das der Rechner ziemlich zäh gelaufen ist.

Wie würdet ihr die geschilderte Situation lösen?
Grüsse
jdk6man
 

Marco13

Top Contributor
Ganz subjektiv fände ich es wohl "schöner", wenn man das Programm schnell (mit einem leeren Fenster) starten könnte, und dann mit einem "Start"-Button der Rechenvorgang gestartet (und währenddessen eine Progressbar angezeigt) wird. Aber ansonsten... sollte die von dir beschriebene Vorgehensweise (ohne explizites Setzen der Priorität) doch auch funktionieren?
 

jdk6man

Mitglied
ich habe eine klasse gemacht die runnable implementiert und ein bufferedimage (public) hat.
Im Konstruktor des Hauptfensters starte ich den Thread mit einem Objekt dieser Klasse. In der Methode paintComponent hatte ich dann mit join auf das ende gewartet, jedoch war das noch zu langsam. Deswegen wollte ich einen Dialog mit einer progressbar machen. Aber deine Idee ist auch gut. Konkret wollte ich alle RGB-Farben durchgehen und anzeigen lassen.
 

Marco13

Top Contributor
In der paintComponent irgendwas anderes machen, als zu zeichnen, ist schonmal grundsätzlich nicht gut. Public-Variablen auch nicht. Kannst du ein KSKB posten, oder den Ablauf etwas genauer beschreiben?
 

jdk6man

Mitglied
hier mal ein beispiel das die situation schemenhaft darstellt:
Java:
public class BildZeichner implements Runnable
{
  public BufferedImage img;
  
  public BildZeichner()
  {
    img=new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
  }
  
  public void run()
  {
    // Zeichenoperationen
  }
}

public class Fenster extends JPanel
{
  private Thread t;
  private BildZeichner bz;
  private JFrame jf;
  
  public Fenster()
  {
    bz=new BildZeichner();
    t=new Thread(bz);
    t.start();
    // Hier hatte ich versucht irgendwie einen Dialog einzubringen
    // Fenster erstellen
  }
  
  @Override protected void paintComponent(Graphics g)
  {
    t.join();
    
    g.drawImage(bz.img, 0, 0, this);
  }
  
  public static void main(String[] args)
  {
    new Fenster();
  }
}

Bitte beurteile meine künste nicht nach diesem Beispiel ;). Das ist nämlich lang nicht meine beste Leistung das habe ich nur irgendwie zusammengeschustert.
Wegen der public Variable: das hab ich bei diesem kleinen Beispiel nicht so eng gesehen. Aber ansonsten ist bei meinen Klassen auch private angesagt.

P.S. Wie macht man das wenn man noch was machen muss in paintComponent. Gibts da eine allgemeine Vorgehensweise?
 

Marco13

Top Contributor
Naja, die paintComponent heißt paintComponent weil sie die Component paintet. Ansonsten hieße sie ja "paintComponentAndDoSomeOtherStuff". Wie man dafür eine "Regel" formulieren sollte, müßte ich jetzt nicht... An einer Stelle rechnet man, und speichert ggf. die Ergebnisse, und in der paintComponent malt man diese Ergebnisse dann.

Genauso "schemenhaft" wie dein Code ;) könnte eine Möglichkeit vielleicht so aussehen:

Java:
public class BildZeichner implements Runnable
{
  public BufferedImage img;
  private Fenster fenster;

  public BildZeichner(Fenster fenster)
  {
    this.fenster = fenster;
    img=new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
  }
  
  public void run()
  {
    // Zeichenoperationen

    // Wenn fertig:
    fenster.setImage(img);
  }
}
 
public class Fenster extends JPanel
{
  private Thread t;
  private BildZeichner bz;
  private JFrame jf;
  
  public Fenster()
  {
    bz=new BildZeichner(this);
    t=new Thread(bz);
    t.start();
    // Hier hatte ich versucht irgendwie einen Dialog einzubringen
    // Fenster erstellen
  }
  
  void setImage(BufferedImage image)
  {
    this.image = image;
    repaint();
  }


  @Override protected void paintComponent(Graphics g)
  {
    super.paintComponent(g);
    if (image != null)
    {
        g.drawImage(bz.img, 0, 0, this);
    }
  }
  
  public static void main(String[] args)
  {
    new Fenster();
  }
}


Und auch auf die Gefahr hin, dass dadurch möglicher... Enthusiasmus ... oder ... Motivation... flöten geht, hab' ich das nochmal mit einem SwingWorker und einem Dialog und so gebaut...
Java:
// For [url]http://www.java-forum.org/awt-swing-swt/89478-designfrage-bei-rechenintensiver-operation.html#post565754[/url]

import javax.swing.*;
import java.util.*;
import java.util.concurrent.*;
import java.awt.*;
import java.awt.image.*;
import java.beans.*;

public class BildZeichnerFenster extends JFrame
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                BildZeichnerFenster bildZeichnerFenster =
                    new BildZeichnerFenster();
                bildZeichnerFenster.setVisible(true);
                bildZeichnerFenster.startCreation();
            }
        });
    }

    private ImagePanel imagePanel;

    public BildZeichnerFenster()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(800,600);
        imagePanel = new ImagePanel();
        getContentPane().add(imagePanel);
    }

    public void startCreation()
    {
        imagePanel.setImage(null);
        ImageWorker imageWorker = new ImageWorker();
        ProgressDialog progressDialog = new ProgressDialog(this, imageWorker);
        imageWorker.execute();
        progressDialog.setVisible(true);

        Image image = null;
        try
        {
            image = imageWorker.get();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
        }
        imagePanel.setImage(image);
    }
}


class ImageWorker extends SwingWorker<Image, Void>
{
    @Override
    public Image doInBackground()
    {
        int w = 500;
        int h = 500;
        int lines = 1000000;
        BufferedImage image=new BufferedImage(w,h, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = image.getGraphics();
        Random random = new Random();
        for (int i=0; i<lines; i++)
        {
            int x0 = random.nextInt(w);
            int y0 = random.nextInt(h);
            int x1 = random.nextInt(w);
            int y1 = random.nextInt(h);
            int r = random.nextInt(256);
            int g = random.nextInt(256);
            int b = random.nextInt(256);
            graphics.setColor(new Color(r,g,b));
            graphics.drawLine(x0,y0,x1,y1);

            int progress = (int)(((float)i/lines) * 100);
            setProgress(Math.min(progress, 100));

        }
        return image;
    }
}


class ProgressDialog extends JDialog implements PropertyChangeListener
{
    private SwingWorker<?,?> swingWorker;
    private JProgressBar progressBar;

    public ProgressDialog(JFrame owner, SwingWorker<?,?> swingWorker)
    {
        super(owner, "Computing...", true);
        this.swingWorker = swingWorker;
        progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        getContentPane().add(progressBar);
        swingWorker.addPropertyChangeListener(this);
        pack();
        setLocationRelativeTo(owner);
    }

    @Override
    public void propertyChange(PropertyChangeEvent event)
    {
        if ("state".equals(event.getPropertyName()) &&
            SwingWorker.StateValue.DONE == event.getNewValue())
        {
            setVisible(false);
            swingWorker.removePropertyChangeListener(this);
            dispose();
        }
        else if ("progress".equals(event.getPropertyName()))
        {
            int progress = (Integer) event.getNewValue();
            progressBar.setValue(progress);
        }
    }

}


class ImagePanel extends JPanel
{
    private Image image;

    public void setImage(Image image)
    {
        this.image = image;
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (image == null)
        {
            g.drawString("Image not computed yet", 16, 16);
        }
        else
        {
            g.drawImage(image, 0, 0, this);
        }
    }

}
 

jdk6man

Mitglied
Diese Idee gefällt mir gut. Aber muss da nicht noch ein lock rein? Swing ist ja nicht Thread-Save.
OK das mit dem paintComponent hab ich verstanden. Ich denke es hängt auch vom Geschmack ab was da rein gehört und was nicht.
Aber es sollte eben nichts mit rein was zu lange dauert oder nicht für das Ergebnis der ähh... Zeichnung wichtig ist. Also wenn man irgendwas abrufen muss mit einer Methode die garantiert nicht blockiert sollte das schon in ordnung sein oder?
Mit SwingWorker hab ich bis jetzt noch nicht gearbeitet, darum war ich mir auch nicht sicher ob es sich fürdiesen Fall anbietet.
 

Marco13

Top Contributor
Da er einem das ganze Dialog-Gewurschtel abnimmt, beitet er sich IMHO auf jeden Fall an. Viele synchronizationsfragen fallen damit auch weg. Eine synchronization braucht man aber im ersten Beispiel glaub' ich auch nicht...
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben