Problem mit der Anzeige von jLabel. Unlesbar wenn der Text geändert wird.

grafsoft

Mitglied
Hallo,

ich möchte den Programmfortschritt in einem Label anzeigen:

Java:
jLabel1.setText("i=" + i);
jLabel1.update(jLabel1.getGraphics());

Das update passiert vielleicht alle 20 Sekunden.

Aber der Text ist unlesbar. Ich möchte aber ein Label haben und nicht einen Fortschrittsbalken, weil ich da auch andere Daten anzeigen möchte.

1679324162830.png
 
Lass mich raten, du machst das Update in einem Thread der nicht der GUI-Thread ist?

Du musst dich auf den GUI-Thread synchronisieren wenn du Aenderungen an den Elementen der GUI machen willst. Bei Swing macht man das entweder mit SwingUtilities.invokeLater oder SwingUtilities.invokeAndWait. Der Unterschied ist dass bei invokeLater das Runnable bei "naechster Gelegenheit" von der GUI ausgefuehrt wird, dass ist dann wenn es der GUI gerade passt. Bei invokeAndWait wird gewartet bis die Operation und erst dann kehrt die Funktion zurueck. Du willst wahrscheinlich invokeLater.
 
Ums kurz zu machen:
Java:
final String text = "i=" + i;
SwingUtilities.invokeLater(() -> jLabel1.setText(text));
wäre ein korrekter Weg, um aus einem Thread heraus das Label zu setzen.
 
Ums kurz zu machen:
Java:
final String text = "i=" + i;
SwingUtilities.invokeLater(() -> jLabel1.setText(text));
wäre ein korrekter Weg, um aus einem Thread heraus das Label zu setzen.

Danke, aber das funktioniert trotzdem nicht. Der neue String überschreibet den alten, aber er löscht ihn vorher nicht.

Ich habe auch das probiert:

Java:
 final String txt = "                ";
 SwingUtilities.invokeLater(() -> jLabel1.setText(txt));
 final String txt1 = "Ferig";
 SwingUtilities.invokeLater(() -> jLabel1.setText(txt1));

Nützt auch nichts.
 
Vielleicht mit AtomicInteger?
Java:
AtomicInteger aInt = new AtomicInteger(0);
...
aInt.set(aInt.get()+1);
SwingUtilities.invokeLater(() -> jLabel1.setText("i="+aInt.get()));
 
Der neue String überschreibet den alten, aber er löscht ihn vorher nicht.
Das ist so nicht richtig. Natürlich ist der alte Text dann weg.

Da wird irgend etwas anderes falsch sein. Hast Du mal mehr Details? Mach mal ein kleinen (minimales) Beispiel, das das Problem zeigt.

Wenn da auch der alte Text noch irgendwie vorhanden ist: Hast Du evtl. mehrere Label, die sich überlappen?
 
Vielleicht mit AtomicInteger?
Braucht man nicht.

Ich weiß ja nicht, was hier gemacht wird, aber das Prinzip ist doch recht einfach:
Java:
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Test {
    private Thread thread = null;
    
    private JButton starter;
    private JLabel label;
    
    private void createAndShowGui() {
        label = new JLabel(" ");
        label.setHorizontalAlignment(JLabel.CENTER);
        label.setFont(label.getFont().deriveFont(72f));
        starter = new JButton("Start");
        starter.addActionListener(e -> start());
        
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(label);
        frame.add(starter, BorderLayout.SOUTH);
        frame.setSize(800, 600);
        frame.setVisible(true);        
    }
    
    private void start() {
        starter.setEnabled(false);
        
        thread = new Thread(() -> {
            Thread current = Thread.currentThread();
        
            for (int i = 0; thread == current && !current.isInterrupted() && i < 10; i++) {
                try { Thread.sleep(1000L); } catch (InterruptedException ex) { current.interrupt(); }
                final String text = "" + (i+1);
                SwingUtilities.invokeLater(() -> label.setText(text));
            }
            thread = null;
            SwingUtilities.invokeLater(() -> starter.setEnabled(true));
        });
        thread.start();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Test().createAndShowGui());
    }
}
 
Kann es sein, dass du irgendwo die Paint-Methode deines Labels oder des Panels dahinter geändert hast?
Bei dir scheint der Hintergrund nicht neu gezeichnet worden zu sein.
 
Ich bilde mir ein dass so etwas auch passieren kann wenn man mit Opaque Eigenschaft das `JLabel`s herumspielt, oder man irgendeinen Hintergrund gesetzt hat der etwas, sagen wir mal, nicht hintergrundig ist.
 
Weird! I have copied and compiled your codes on my computer with "old" JDK version 9.0.4 and it works perfectly
 

Anhänge

  • test.png
    test.png
    4,9 KB · Aufrufe: 0
Ich habe Atomicinteger ausprobiert, Scrollbars, Thread.sleep, repaint, revalidate ...

Das update passiert erst wenn alles fertig ist. Irgendwas mache ich prinzipiell falsch.
 
Das update passiert erst wenn alles fertig ist.
Was genau machst du denn? Das hört sich jetzt danach an, dass Du im UIThread die UI mehrfach anpasst über eine gewisse Zeit und dabei den UI Thread blockierst. Und die UI wird erst neu gemalt, wenn Du den UI Thread frei gegeben hast und dieser damit die Events zum Neu Malen abzuarbeiten.
 
Ich bilde mir ein dass so etwas auch passieren kann wenn man mit Opaque Eigenschaft das `JLabel`s herumspielt, oder man irgendeinen Hintergrund gesetzt hat der etwas, sagen wir mal, nicht hintergrundig ist.
Stimmt, da war was. Ich hatte mal alle Objekte auf transparent gesetzt bis ganz nach hinten, da ging's ebenfalls daneben. Man möchte meinen, dass ein Frame trotzdem gezeichnet wird 🙂
 
Ich mache nichts besonderes: Menü - Dateiauswahl - dann werden in einer anderen Klasse Wortgruppen extrahiert (1-5 Sekunden), diese in einen JSON zusammengesammelt und in 100-Stück-Paketen von einer anderen Klasse an einen Server geschickt (ca. 10 Sekunden bis zur Antwort). Der Bildschirm ist einfach, hat ein Menü, eine Textarea, ein Label und jetzt auch 2 Scrollbars. Keine Hintergründe verändert, nichts ist verdeckt.. Ich möchte einfach nur den Fortschritt irgendwie anzeigen.
 
Ist halt schon ein biss viel zum lesen ...

Java:
private void OpenActionPerformed(java.awt.event.ActionEvent evt) {                                     
        
        IniFile iniFile = new IniFile("config.ini");
        String startdir = iniFile.getProperty("Directories", "Select");
        if (startdir=="")
        {
            startdir=System.getProperty("user.dir");
        }
        startdir=startdir + "/.";
        FileChooser.setCurrentDirectory(new File(startdir));
        
        int returnVal = FileChooser.showOpenDialog(this);
        if (returnVal == FileChooser.APPROVE_OPTION) {
            File file [] = FileChooser.getSelectedFiles();
            URLPoster u = new URLPoster ();
            u.setbaseurl(urlbase);
            String fname;
            String dirname = "";
            jProgressBar1.setMinimum(0);
            jProgressBar1.setMaximum(file.length);
            jProgressBar1.setStringPainted(true);
            for (int iz = 0; iz < file.length; iz++) {
                try {
                    jProgressBar1.setValue(iz);
                    jProgressBar1.repaint();
                    jPanel1.repaint();
                    jPanel1.revalidate();
                    
                    // synchronized (this) {
                        Thread.sleep(100L);
                }
                catch (Exception e)
                {
                    System.out.println(e.getMessage());
                }
                
                fname = file [iz].getAbsolutePath();
                ltext.append ("file "+ fname + "\n");
                Long l = file [iz].lastModified();
                Date date=new Date(l);
                SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dateText = df2.format(date);
                String getJSON = u.postdata("g_checkfile.php","f='" + fname + "'&date=" + dateText);
                dirname=file [iz].getPath();
                ltext.append (getJSON + "\n");
                ltext.setCaretPosition(ltext.getDocument().getLength());
                ltext.update(ltext.getGraphics());
                Object obj = JSONValue.parse(getJSON);
                JSONArray jarray = (JSONArray)obj;
                if (jarray==null) { continue; }
                String no =  jarray.get(1).toString();
                String isda = jarray.get(0).toString();
                Boolean doit;
                doit = true;
                if (isda.length() > 3) { doit = false; }
                System.out.println (fname);
                System.out.println (getJSON + " no=" + no + " exists=" + isda + " " + isda.length() + " => " + doit);
                String strout = "[";
                Integer schleife;
                if (doit == true)
                {
                    try
                    {
                        Path filePath = Path.of(fname);
                        String cc = Files.readString(filePath);
                        if (cc.contains("="))
                        {
                            cc=cc.replace("=C3=84", "Ä");
                            cc=cc.replace("=C3=96", "Ö");
                            cc=cc.replace("=C3=9C", "Ü");
                            cc=cc.replace("=C3=A4", "ä");
                            cc=cc.replace("=C3=B6", "ö");
                            cc=cc.replace("=C3=BC", "ü");
                            cc=cc.replace("=C3=9F", "ß");
                        }
                        
                        NGram ngram = new NGram();
                        List<String> ngrams = ngram.generateNGrams (cc,4);
                        schleife=0;
                        System.out.println("ngrams=" + ngrams.size());
                        jProgressBar2.setMinimum(0);
                        jProgressBar2.setMaximum(ngrams.size());
                        jProgressBar2.setStringPainted(true);
                        for (int i=0; i<ngrams.size()-1; i++)
                        {
                             try {
                                jProgressBar2.setValue(i);
                                jProgressBar1.repaint();
                                Thread.sleep(10);
                            } catch (InterruptedException ignoredException) {}
                            String s = ngrams.get(i);
                            if (strout.length()>1) { strout=strout + ","; }
                            strout = strout + '"' + s + '"';
                            // schleife++;
                            if (schleife==maxschleife)
                            {
                                gibaus (strout,u,dateText);
                                strout = "[";
                                
                                System.out.println("i=" + i);
                                jLabel1.setText("i=" + i);
                                jLabel1.update(jLabel1.getGraphics());
                                schleife=0;
                            } 
                            final String txt1 = "i=" + i;
                            SwingUtilities.invokeLater(() -> jLabel1.setText(txt1));
                            schleife++;
                        if (i >= maxngrams) {break;}
                        }
                        gibaus (strout,u,dateText);
                        final String txt = "                ";
                        SwingUtilities.invokeLater(() -> jLabel1.setText(txt));
                        final String txt1 = "Ferig";
                        SwingUtilities.invokeLater(() -> jLabel1.setText(txt1));
                    }
                    catch (Exception e)
                    {
                        System.out.println (e.getMessage());
                    }
                }
            }
            iniFile.setProperty("Directories", "Select",dirname);
            iniFile.save();
            
            jProgressBar1.setValue(file.length);
            jProgressBar1.repaint();
        } else {
            System.out.println("File access cancelled by user.");
        }
        
        JOptionPane.showConfirmDialog(null, "Fertig!", "Schau schau ...",
                JOptionPane.OK_OPTION, JOptionPane.INFORMATION_MESSAGE);
    }
 
Ist halt schon ein biss viel zum lesen ...

Java:
private void OpenActionPerformed(java.awt.event.ActionEvent evt) {                                    
       
        IniFile iniFile = new IniFile("config.ini");
        String startdir = iniFile.getProperty("Directories", "Select");
        if (startdir=="")
        {
            startdir=System.getProperty("user.dir");
        }
        startdir=startdir + "/.";
        FileChooser.setCurrentDirectory(new File(startdir));
       
        int returnVal = FileChooser.showOpenDialog(this);
        if (returnVal == FileChooser.APPROVE_OPTION) {
            File file [] = FileChooser.getSelectedFiles();
            URLPoster u = new URLPoster ();
            u.setbaseurl(urlbase);
            String fname;
            String dirname = "";
            jProgressBar1.setMinimum(0);
            jProgressBar1.setMaximum(file.length);
            jProgressBar1.setStringPainted(true);
            for (int iz = 0; iz < file.length; iz++) {
                try {
                    jProgressBar1.setValue(iz);
                    jProgressBar1.repaint();
                    jPanel1.repaint();
                    jPanel1.revalidate();
                   
                    // synchronized (this) {
                        Thread.sleep(100L);
                }
                catch (Exception e)
                {
                    System.out.println(e.getMessage());
                }
               
                fname = file [iz].getAbsolutePath();
                ltext.append ("file "+ fname + "\n");
                Long l = file [iz].lastModified();
                Date date=new Date(l);
                SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dateText = df2.format(date);
                String getJSON = u.postdata("g_checkfile.php","f='" + fname + "'&date=" + dateText);
                dirname=file [iz].getPath();
                ltext.append (getJSON + "\n");
                ltext.setCaretPosition(ltext.getDocument().getLength());
                ltext.update(ltext.getGraphics());
                Object obj = JSONValue.parse(getJSON);
                JSONArray jarray = (JSONArray)obj;
                if (jarray==null) { continue; }
                String no =  jarray.get(1).toString();
                String isda = jarray.get(0).toString();
                Boolean doit;
                doit = true;
                if (isda.length() > 3) { doit = false; }
                System.out.println (fname);
                System.out.println (getJSON + " no=" + no + " exists=" + isda + " " + isda.length() + " => " + doit);
                String strout = "[";
                Integer schleife;
                if (doit == true)
                {
                    try
                    {
                        Path filePath = Path.of(fname);
                        String cc = Files.readString(filePath);
                        if (cc.contains("="))
                        {
                            cc=cc.replace("=C3=84", "Ä");
                            cc=cc.replace("=C3=96", "Ö");
                            cc=cc.replace("=C3=9C", "Ü");
                            cc=cc.replace("=C3=A4", "ä");
                            cc=cc.replace("=C3=B6", "ö");
                            cc=cc.replace("=C3=BC", "ü");
                            cc=cc.replace("=C3=9F", "ß");
                        }
                       
                        NGram ngram = new NGram();
                        List<String> ngrams = ngram.generateNGrams (cc,4);
                        schleife=0;
                        System.out.println("ngrams=" + ngrams.size());
                        jProgressBar2.setMinimum(0);
                        jProgressBar2.setMaximum(ngrams.size());
                        jProgressBar2.setStringPainted(true);
                        for (int i=0; i<ngrams.size()-1; i++)
                        {
                             try {
                                jProgressBar2.setValue(i);
                                jProgressBar1.repaint();
                                Thread.sleep(10);
                            } catch (InterruptedException ignoredException) {}
                            String s = ngrams.get(i);
                            if (strout.length()>1) { strout=strout + ","; }
                            strout = strout + '"' + s + '"';
                            // schleife++;
                            if (schleife==maxschleife)
                            {
                                gibaus (strout,u,dateText);
                                strout = "[";
                               
                                System.out.println("i=" + i);
                                jLabel1.setText("i=" + i);
                                jLabel1.update(jLabel1.getGraphics());
                                schleife=0;
                            }
                            final String txt1 = "i=" + i;
                            SwingUtilities.invokeLater(() -> jLabel1.setText(txt1));
                            schleife++;
                        if (i >= maxngrams) {break;}
                        }
                        gibaus (strout,u,dateText);
                        final String txt = "                ";
                        SwingUtilities.invokeLater(() -> jLabel1.setText(txt));
                        final String txt1 = "Ferig";
                        SwingUtilities.invokeLater(() -> jLabel1.setText(txt1));
                    }
                    catch (Exception e)
                    {
                        System.out.println (e.getMessage());
                    }
                }
            }
            iniFile.setProperty("Directories", "Select",dirname);
            iniFile.save();
           
            jProgressBar1.setValue(file.length);
            jProgressBar1.repaint();
        } else {
            System.out.println("File access cancelled by user.");
        }
       
        JOptionPane.showConfirmDialog(null, "Fertig!", "Schau schau ...",
                JOptionPane.OK_OPTION, JOptionPane.INFORMATION_MESSAGE);
    }
Oh man, warum schreibst du nicht gleich alles in einer Zeile? Dann versteht man den Spagetticode gleich noch weniger.
 
Das, was Du da derzeit machst, läuft alles in dem UI Thread. Alles, was auch nur etwas merkbar Zeit brauchen könnte, muss ich separate Threads verlagert werden. Und das machst Du nicht.

Du könntest die Methode komplett in eine andere Methode machen und dann in dieser Methode nur noch die andere Methode in einem anderen Thread starten. Das könnte schon ausreichen, da Du ja schon invokeLater Aufrufe hast für UI Updates.
 
Das, was Du da derzeit machst, läuft alles in dem UI Thread. Alles, was auch nur etwas merkbar Zeit brauchen könnte, muss ich separate Threads verlagert werden. Und das machst Du nicht.

Du könntest die Methode komplett in eine andere Methode machen und dann in dieser Methode nur noch die andere Methode in einem anderen Thread starten. Das könnte schon ausreichen, da Du ja schon invokeLater Aufrufe hast für UI Updates.
Vielen Dank, jetzt weiß ich wo ich mich weiter schlau machen sollte.
 
Weird! I have copied and compiled your codes on my computer with "old" JDK version 9.0.4 and it works perfectly
Sure, there is nothing unusual in this code. It should work with Java 8 as well (if you replace the lambda expressions with anonymous inner classes, it should even work with Java 1.5 or earlier). Just make sure that anything UI-related runs in the Event Dispatch Thread (that is what invokeLater is for) and you are fine with respect to multi-threading.

EDIT: entfernt, weil ich die Diskussion von @KonradN und @grafsoft übersehen habe.
 
Ich habe jetzt einen Swingworker eingebaut, der den zeitaufwendigen Teil macht (derzeit nur warten).

Der Progressbar wird noch immer nicht upgedated.

Ich habe jetzt ein ganz simples Beispiel gemacht:

Java:
  public void progress(int value) {
        SwingUtilities.invokeLater(() -> jProgressBar1.setValue(value));
        SwingUtilities.invokeLater(() -> jProgressBar1.repaint());
    }
    
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        jProgressBar1.setMinimum(0);
        jProgressBar1.setMaximum(100);
        jProgressBar1.setStringPainted(true);
        Hackler l = new Hackler();
        SwingWorker work = l.createWorker();
        work.execute();
        // Wait for it to finish
        while (!work.isDone()) {
            // Show Progress
            try {
                int iProgress = work.getProgress();
                System.out.println("Progress %" + iProgress);
                /* Versuch 1:
                SwingUtilities.invokeLater(() -> jProgressBar1.setValue(iProgress));
                SwingUtilities.invokeLater(() -> jProgressBar1.repaint());
                */
                // Versuch 2, in einem separaten sub:
                // progress(iProgress);
                // Versuch 3:
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        jProgressBar1.setValue(iProgress);
                    }
                });
                Thread.sleep(500);
            }
            catch (Exception ex) {
                System.err.println(ex);
            }
        } // End of Loop: while (!work.isDone())
        
    }

System.out.println zeigt den Fortschritt an, aber der Progress bar will sich nicht bewegen, erst am Schluss.
 
Java:
 while (!work.isDone()) {

Damit sabotierst du dich selbst. Du befindest dich an dieser Stelle im UI thread und zwingst diesen dazu, zu warten, bis der Worker-Thread beendet ist. Während dieser Zeit wird die Anzeige nicht geändert, egal was du hier machst. Änderungen werden erst sichtbar, wenn der UI-Thread einmal komplett durchgelaufen ist, erst dann wird was immer auch gemacht wurde auf den Bildschirm angezeigt.

So wäre die grundsätzliche Vorgehensweise:
Java:
class MyForm implemants DisplaysProgressInterface {

    void handleTheButton(ActionEvent e) {
        new ASwingWorker(this).start();
    }


    @Override
    void setBusy(boolean b) {
      //was auch immer, Glasspane setzen, Mauszeiger auf "busy" setzen oder auch nicht ...
    }
    
    @Override
    void setProgressTo(int current, int max) {
      //Hier den Zähler anzeigen, am Besten mit invokeLater, auch wenn die Events eh erst aus gelöst werden, wenn der UI Thread seine aktuelle Arbeit beendet hat.
    }
    @Override
    void showCompleted() {
      //z.b. Zähler auf 100 setzen
      setBusy(false);
    }
  }
  private class ASwingWorker extends SwingWorker<Integer, Integer> {
    ASwingWorker(DisplaysProgressInterface owner) {
    }
    @Override
    protected Integer doInBackground() throws Exception {
      owner.setBusy(true);
      owner.setProgressTo(0,100);
      //Tun, was getan werden muss
      //hin und wieder
      owner.setProgressTo(progressCounter,100);
    }

    @Override
    protected void done() {
      owner.showCompleted();
    }
  }

Auch wenn einige da widersprechen mögen ... Arbeiten, die nicht direkt die UI betreffen, pack ich nur ungern in einen SwingWorker, sondern lieber in einen klassischen Thread. Die Vorgehensweise wäre die selbe. Am Ende der Run-Methode teilt man dem Owner mit, dass man fertig ist.

So oder so, der UI-Thread DARF NICHT auf den WorkerThread warten, sonst ist die Oberfläche wieder blockiert.

Wie du hier siehst, ändert der Workerthread auch nicht direkt die Labels im Formular sondern spricht Methoden in einem Interface an.
Vorteil: Hinter dem Interface könnte auch eine einfache System.out Ausgabe stehen, den Worker hat im Normalfall nicht zu interessieren, wie genau der Progress angezeigt wird, wichtig ist nur, DASS es passiert. Um die Implementierung kümmert sich jemand anderes.
 
Danke, danke!! Jetzt hab ichs endlich kapiert. Ich stecke die Änderungen der Anzeige in einen ActionListener und den rufe ich mit einem Timer auf. Funktioniert flauschig.
 
Genau! Ist das schlecht? Newbies stellen halt solche Fragen ...
Nicht grundsätzlich. Mir war nur nicht klar, was du damit wolltest (das mit dem Timer hatte ich beim ersten Mal übersehen, sorry.). So wie ich's gewohnt bin, entscheidet der Worker Timer selbst, wann er die Anzeige aktualisieren lassen will. Deine ist einfach nur eine andere Herangehensweise, mag je nach Anwendung vielleicht sogar sinnvoller sein.
 

Zurück
Oben