Как обновить JLabel, отображающий изображение в цикле while? - PullRequest
0 голосов
/ 07 ноября 2018

Итак, я пытаюсь создать очень простой удаленный просмотрщик, и сейчас он в основном состоит из клиента, который делает снимок экрана, отправляет его на сервер, сервер превращает его в изображение и сохраняет его как * 1001. *, а затем отображает это, а затем повторять каждые три секунды.

Все работает нормально, пока мы не доберемся до отображаемой части. Когда я первоначально загружаю программу, если я установлю в окне изображения значение image.jpg, она будет работать (пока существует image.jpg, то есть программа запускалась более одного раза). Однако, когда я пытаюсь установить его / обновить через мой Runnable, он не работает.

Примечание: элементы обновляются, когда вы помещаете их в Runnables, так что это явно не проблема.

Вот код сервера (получающий байтовый массив изображения):

Thread thread = new Thread(new Runnable() {
    public void run() {
        while (true) {
            try {
                // Get client and receive byte array
                ServerSocket server = new ServerSocket(2282);
                Socket socket = server.accept();
                DataInputStream inputData = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
                // Read bytes to byte array
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte buffer[] = new byte[1024];
                for (int s; (s = inputData.read(buffer)) != -1;) {
                  baos.write(buffer, 0, s);
                }
                byte result[] = baos.toByteArray();
                // Create bufferedImage from byte array
                BufferedImage bufferedImage = null;
                ByteArrayInputStream bais = new ByteArrayInputStream(result);
                bufferedImage = ImageIO.read(bais);
                // Save image
                File outputfile = new File("image.jpg");
                ImageIO.write(bufferedImage, "jpg", outputfile);
                // Update image box
                BufferedImage imageIcon = ImageIO.read(new File("image.jpg"));
                BufferedImage resizedImage = resize(imageIcon, 440, 820); // Resizes image with parameters of (BufferedImage img, int height, int width)
                imageBox = new JLabel(new ImageIcon(resizedImage));
                imageBox.setBounds(10, 11, 826, 446);
                frame.getContentPane().add(imageBox);
                // Close everything
                server.close();
                socket.close();
                inputData.close();
                baos.close();
                bais.close();
            }
            catch (IOException e) {
                System.out.println(e);
            }
        }
    }
});
thread.start();

И хотя я очень сомневаюсь, что с клиентом что-то не так, я также предоставил это, если вы захотите;

while (true) {
    System.out.println("started");
    // Capture the image
    Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
    BufferedImage capture = robot.createScreenCapture(screenRect);

    // Convert the image to a byte array
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write(capture, "jpg", baos);
    byte[] bytes = baos.toByteArray();

    // Send byte array over socket connection
    Socket socket = new Socket("CamdensProgrammingDesktop", 2282);
    DataOutputStream outputData = new DataOutputStream(socket.getOutputStream());
    outputData.write(bytes);
    socket.close();
    outputData.close();

    TimeUnit.SECONDS.sleep(3);
}

Итак, если вы запустите этот код, вы должны получать image.jpg, создаваемый каждые три секунды, но почему не обновляется окно изображения?

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Оказывается, проблема была в том, что я создавал новый JLabel каждый раз, когда изображение должно было отображаться, что не работало. Чтобы решить эту проблему, я установил JLabel на image.jpg при запуске программы, затем в цикле while используйте .setIcon(), чтобы легко изменить значок.

0 голосов
/ 07 ноября 2018

У вас есть несколько проблем в коде выше:

  • Вы создаете компоненты Swing и обновляете основной графический интерфейс (JFrame) из фонового потока - небезопасно. Используйте SwingWorker, чтобы помочь вам сохранить код, который должен находиться в фоновом режиме, отдельно от кода, который должен вызываться в потоке событий Swing (EDT). Пожалуйста, смотрите Урок: Параллелизм в Swing , чтобы узнать, почему это важная проблема и как SwingWorker может помочь вам ее исправить.
  • Вы добавляете компонент в контейнер и не вызываете revalidate или repaint для контейнера
  • Вы вызываете setBounds(...) для компонента Swing, предполагая, что вы используете пустой макет где-то в графическом интерфейсе, и что-то, что вы определенно не хотите делать, так как это делает отладку, обновление и улучшение графического интерфейса очень сложно, и рискует, что ваша программа плохо отображается на других платформах.
  • Вы создаете новый JLabel без необходимости - оставьте один JLabel и поместите его в графический интерфейс. Затем после создания изображения просто поменяйте местами значок ярлыка. Нет необходимости перекрашивать или проверять заново, когда вы делаете это.

Я бы сам создал SwingWorker<Void, Icon>, чтобы он производил значки изображений из метода doInBackground(), а затем перекачивал значки в графический интерфейс, используя пару рабочих методов публикации / процесса.

Например:

SwingWorker<Void, Icon> worker = new SwingWorker<Void, Icon>() {
    // this is called in background thread
    @Override
    public Void doInBackground() throws Exception {
        boolean connectionStillGood = true;
        while (connectionStillGood) {
            //....
            // code to get image data from socket goes here
            // ....
            // update connectionStillGood value

            BufferedImage bufferedImage = ImageIO.read(....);
            Icon icon = new ImageIcon(bufferedImage);
            publish(icon);  // send icon to process method
        }
        return null;
    }

    @Override
    protected void process(List<Icon> chunks) {
        for (Icon icon : chunks) {
            imageBox.setIcon(icon);
        }
    }

};
worker.execute();

Более конкретный пример (сначала обязательно запустите класс ImageReceiver):


ImageReceiver.java

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.swing.*;

public class ImageReceiver extends JPanel {
    private static final long serialVersionUID = 1L;
    public static final int PORT_ADDR = 4040;
    private static final int PREF_W = 400;
    private static final int PREF_H = PREF_W;
    private JLabel imageBox = new JLabel();

    public ImageReceiver() {
        MyWorker worker = new MyWorker();
        worker.addPropertyChangeListener(new WorkerListener());
        worker.execute();

        setLayout(new BorderLayout());
        JScrollPane scrollPane = new JScrollPane(imageBox);
        scrollPane.getViewport().setPreferredSize(new Dimension(PREF_W, PREF_H));
        add(scrollPane);
    }

    private class WorkerListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                MyWorker worker = (MyWorker) evt.getSource();
                try {
                    worker.get();
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private class MyWorker extends SwingWorker<Void, Icon> {
        // this is called in background thread
        @Override
        public Void doInBackground() throws Exception {
            try (ServerSocket server = new ServerSocket(PORT_ADDR);
                    Socket socket = server.accept();
                    InputStream is = socket.getInputStream();) {

                while (socket != null && !socket.isClosed()) {
                    BufferedImage img = ImageIO.read(is);
                    if (img != null) {
                        publish(new ImageIcon(img));
                    }
                }
            } catch (IOException e) {
                throw e;
            }
            return null;
        }

        @Override
        protected void process(List<Icon> chunks) {
            for (Icon icon : chunks) {
                imageBox.setIcon(icon);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Receiver");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(new ImageReceiver());
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }
}

ImageProducer.java

import java.awt.image.BufferedImage;
import java.io.*;
import java.net.*;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;

public class ImageProducer {
    public static final String BASE = "https://upload.wikimedia.org/wikipedia/commons/";
    public static final String[] IMG_PATHS = { 
            "7/74/VELEZ_SARSFIELD.png",
            "c/c4/Princeton_Univ_pub-mark.png", 
            "9/92/Union_escudo_antiguo.png", 
            "8/88/Faelog.jpg",
            "8/84/Club-social-y-deportivo-mu%C3%B1iz.png", 
            "0/08/6931st_CSC.jpg",
            "f/ff/Coat_of_arms_of_Mielec.png", 
            "6/64/Francocanadiense.jpg",
            "5/58/Kitty_cat_council.jpg", 
            "thumb/1/12/Cear2002.jpg/400px-Cear2002.jpg",
            "9/94/Wappen_von_Arkantos.png", 
            "3/39/Escudo_chaca_Taq.png", 
            "a/a2/USBCcrest.jpg",
            "0/07/351st_Avn_Co_pocket_patch_1.jpg", 
            "0/06/Steinbach_Lebach_wappen.png",
            "f/f9/90th_Avn_Co_MedHelpatch.jpg" };

    public static void main(String[] args) {
        String imagePath = "";
        try (Socket socket = new Socket("localhost", ImageReceiver.PORT_ADDR);
                OutputStream os = socket.getOutputStream();
                InputStream is = socket.getInputStream()) {
            for (String path : IMG_PATHS) {
                imagePath = BASE + path;
                URL inputUrl = new URL(imagePath);
                BufferedImage img = ImageIO.read(inputUrl);
                ImageIO.write(img, "PNG", os);
                TimeUnit.SECONDS.sleep(4);
            }

        } catch (IOException e) {
            System.out.println("Image Path: " + imagePath);
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
...