WebHU - Programozási kérdések és válaszok

Szálalvás az actionPerformed metóduson belül

Először is szeretném elmondani, hogy tisztában vagyok vele, hogy ez a megközelítés helytelen, ezért pusztán kíváncsiságból teszem fel ezt a kérdést. Tegyük fel, hogy van egy ilyen swing alkalmazásom:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ThreadSleeping {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JButton button = new JButton("Load");
    JLabel label = new JLabel();

    public ThreadSleeping() {
        panel.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Tulips.jpg"));
                System.out.println("Tulips painted");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                label.setIcon(new ImageIcon(
                        "C:/Users/Public/Pictures/Sample Pictures/Koala.jpg"));
                System.out.println("Koala painted");

            }
        });

        frame.add(panel, BorderLayout.NORTH);
        frame.add(label, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(1024, 768);
        // frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new ThreadSleeping();
            }
        });
    }
}

Alapvetően, amikor rákattintok egy Load gombra, azt várom, hogy Tulips.jpg kép jelenik meg, majd a GUI lefagy 2 másodpercre, és utána Koala.jpg kép jelenik meg. De ami történik: rákattintok a gombra, a GUI lefagy 2 másodpercre és Koala.jpg jelenik meg. Nem Tulips.jpg előtte. De ami megzavar, az az, amikor beraktam a System.out.println("Tulips painted"); és System.out.println("Koala painted");. Tehát amikor rákattintok a gombra, kiírja a "Tulips painted" és 2 másodperc múlva a "Koala painted" feliratot. Valaki meg tudná nekem mondani, hogy mi folyik itt? Üdvözlettel.


  • Vissza kell térnie a actionPerformed helyről, hogy a swing frissíthesse a grafikus felületet. 24.03.2013
  • ha alszik az edt, az ebből eredő helytelen viselkedés alapvetően kiszámíthatatlan. Nincs sok értelme bármit is belemagyarázni. Egyszerűen játssz a szabályok szerint, és fordíts időt és energiát valami hasznosra :-) 24.03.2013
  • @kleopatra – teljes mértékben nem értek egyet. Nagyon tanulságos megérteni, miért valami rossz gyakorlat, sokkal értékesebb, mint egyszerűen meghallgatni, ahogy mások azt mondják: Ne!. Ha egyszerűen a szabályok szerint játszol, soha nem érsz fel semmire – értsd meg, miért léteznek a szabályok, és használd ezt a tudást a javadra. 24.03.2013
  • @dimo414 igazad van, ki kellett volna hagyni az utolsó mondatot – mivel én magam is nagy rajongója vagyok a miértnek ;-) 25.03.2013

Válaszok:


1
  1. ebben az esetben működik, mert programozottan lefagyasztja a Swing grafikus felhasználói felületet, de van/nincs másik frissítés, más JComponent(ek)

  2. nem működik abban az esetben, ha a Swing grafikus felületén van néhány újabb frissítés, a Thread.sleep(int) freeze Event Dispatch Thread,

  3. alapértelmezés szerint a JComponents XxxModels összes frissítése soha nem lesz látható a JComponents view

  4. példa, amíg az alvás véget nem ér, az összes frissítést elveszíti GUI

24.03.2013
  • fontosak a natív operációs rendszer, a használt Look and Feel és az API-ban Thread biztonságosként deklarált metódusok, majd alapértelmezés szerint WinXP, Java6, MetalLoookAndFeel és metódusok setText(), append() ... alapértelmezés szerint működik, de a Java7-ben ezek egy része megváltozott logikák 24.03.2013

  • 2

    A lényeg, amit a megjegyzésemben ki akartam fejteni:

    ha alszik az edt, az ebből eredő helytelen viselkedés alapvetően kiszámíthatatlan.

    Kiszámíthatatlan abban az értelemben, hogy nem tudhatod, hogy mi fog történni vagy sem. Nem tehetünk mást, mint kitalálni...

    Ennek technikai oka az, hogy a legtöbb UI frissítés nem azonnal, hanem ütemezetten történik: nem igazán tudhatjuk, mi vár a sorban mögöttünk. Ne feledje: ez csak egy sáv, és amikor a Performed akcióban van, mi ülünk benne.

    Oktatási okokból az alábbi kód az eredeti kód, néhány sorral a megjegyzések eltávolításához/megjegyzésekhez a különböző forgatókönyvek bemutatása érdekében.

    • [0] az ikon visszaállítása alvás előtt/után: ahogy már észrevette, az első nem jelenik meg, bár a tulajdonság foglalt. Technikai ok: a vizuális frissítés a következőn keresztül történik: label.repaint(), amely az EDT-n van ütemezve az utóbbi feldolgozáshoz (ahogyan az api-dokumentumban szerepel)
    • [1] átfutva az api doc-ot, egy másik módszert is észreveszünk: paintImmediately(...) ami dokumentáltan pontosan azt csinálja, amit a neve mond, és megengedett - ahogyan az EDT-n is - megengedik, hogy hívják. Sikernek tűnik, megjelenik a sárga ikon.
    • [2], de várj: a Borderline közepén a címke úgyis kitölti azt a területet, függetlenül attól, hogy van-e rajta ikon vagy sem. Próbáljuk meg egy olyan régióba helyezni, amely újratervezést igényel, mivel f.i. délre. Vissza a térre [0], a sárga ikon nem jelenik meg.
    • [3] megvizsgálva a setIcon(..) forrását, kiderül, hogy az elrendezés... ismét ütemezett. Az [1] négyzetben megtanultuk, hogy a dolgokat azonnal megtörténhetõvé tudjuk kényszeríteni, ha az elrendezés a invalidate() / validate() pár lenne. Bingó, sárga ikon még délen is.
    • [4] csúnya alosztály, amely ütemezi az ikon tulajdonságainak beállítását (megjegyzés: bár itt van kitalálva, a szerződésében semmi sem akadályozza meg az alosztályokat!). Mivel a tulajdonság még nincs beállítva, a sárga nem jelenik meg, vissza a négyzethez [0]

    A nap végén (de elalvás előtt az EDT :-)) egyszerűen nem lehet megbízhatóan megjósolni a vizuális eredményt alvás közben. A látvány pedig csak a jég csúcsa...

    /**
     * Unpredictable behaviour when sleeping the EDT.
     * http://stackoverflow.com/q/15600203/203657
     * 
     * [0] run as-is: property set but yellow not showing
     * [1] uncomment paintImmediately: yellow showing in center
     * [2] add label to south: yellow not showing
     * [3] force immediate in-/validation: yellow showing in south
     * [4] subclass label with invoked property setting: 
     *       property not set, yellow not showing
     * 
     */
    public class ThreadSleeping {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JButton button = new JButton("Load");
        JLabel label = new JLabel() {
    // [4] subclass implemented to schedule the property setting         
    //        @Override
    //        public void setIcon(final Icon icon) {
    //            SwingUtilities.invokeLater(new Runnable() {
    //                public void run() {
    //                    superSetIcon(icon);
    //                    
    //                }
    //            });
    //        }
    //        
    //        protected void superSetIcon(Icon icon) {
    //            super.setIcon(icon);
    //        }
    //        
        };
    
        public ThreadSleeping() {
            panel.add(button);
    
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    Icon firstIcon = new FixedIcon(Color.YELLOW, 100, 100);
                    Icon secondIcon = new FixedIcon(Color.RED, 500, 500);
                    label.setIcon(firstIcon);
                    // check if the property is already set
                    System.out.println(label.getIcon());
                    // following lines try to force the output before going to sleep
                    // [3] paintImmediately + force immediate re-layout 
                    //  label.invalidate();
                    //  label.getParent().validate();
                    // {1] paintImmediately (fine for center, no effect for south)
                    // ((JComponent) label.getParent()).paintImmediately(0, 0, 5000, 5000);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    label.setIcon(secondIcon);
    
                }
            });
    
            frame.add(panel, BorderLayout.NORTH);
            // un/comment one of the following, placing the
            // label either in CENTER (= sized to fill)
            frame.add(label, BorderLayout.CENTER);
            // [2] or in SOUTH (= sized to pref)
            // frame.add(label, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.setSize(1024, 768);
            frame.setVisible(true);
        }
    
        /**
         * Simple fixed size Icon filling its area.
         */
        public static class FixedIcon implements Icon {
    
            private Color background;
            private int width;
            private int height;
    
            public FixedIcon(Color background, int width, int height) {
                this.background = background;
                this.width = width;
                this.height = height;
            }
            @Override
            public void paintIcon(Component c, Graphics g, int x, int y) {
                g.setColor(background);
                g.fillRect(0, 0, width, height);
            }
    
            @Override
            public int getIconWidth() {
                return width;
            }
    
            @Override
            public int getIconHeight() {
                return height;
            }
            @Override
            public String toString() {
                return "[" + background + " w/h " + width + "/" + height + "]";
            }
    
    
        }
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new ThreadSleeping();
                }
            });
        }
    }
    
    25.03.2013
  • Köszönöm a választ, sokat segített. Következtetés: Ilyen esetekben mindig ragaszkodnunk kell a lengésidőzítőhöz vagy a lengőmunkáshoz, hogy elkerüljük a grafikus felület lefagyását, igaz? 25.03.2013
  • Új anyagok

    A rádiógomb ellenőrzött eseményének használata a jQueryben
    Ebben a cikkben látni fogjuk, hogyan kell dolgozni a jquery választógombbal ellenőrzött eseményeivel. A választógombok HTML gombok, amelyek segítenek kiválasztani egyetlen értéket egy csoportból...

    Körkörös függőségek megoldása terraformban adatforrásokkal – lépésről lépésre
    Mi az a körkörös függőségek Dolgozzunk egy egyszerű eseten, amikor az SQS-sor és az S3-vödör közötti körkörös függőség problémája van egy egymástól függő címkeérték miatt. provider..

    Miért érdemes elkezdeni a kódolást 2023-ban?
    01100011 01101111 01100100 01100101 — beep boop beep boop Világunk folyamatosan fejlődik a technológia körül, és naponta fejlesztenek új technológiákat a valós problémák megoldására. Amint..

    🎙 Random Noise #2  – Örökbefogadás és hit
    az analitika íratlan világának gondozása Szeretné, hogy ezek a frissítések a postaládájába kerüljenek? Iratkozzon fel itt . "Ha önvezető autókat gyártanak, akkor mi miért ne..

    A legrosszabb politika és prediktív modellek májátültetésre jelöltek számára az Egyesült Államokban
    A máj (vagy óangolul lifer) az emberi test legnehezebb belső szervére utal, amely csendesen működik a nap 24 órájában. Mit csinál a máj? 500 feladatot hajt végre a szervezet egészségének..

    5 webhely, amely 2022-ben fejleszti front-end fejlesztői készségeit
    Frontendmentor.io A tényleges projektek létrehozásával a Frontendmentor.io segítséget nyújt a front-end kódolási képességeinek fejlesztésében. A kódolást azután kezdheti meg, hogy..

    Mikor kell használni a Type-t az interfészhez képest a TypeScriptben?
    A TypeScript a JavaScript gépelt szuperkészlete, amely statikus gépelést ad a nyelvhez. Ez megkönnyíti a robusztus és karbantartható kód írását azáltal, hogy a hibákat a fordítási időben..