Невозможно обновить значение JProgressBar из другого потока при добавлении других элементов

У меня проблема с отображением текущего состояния (в JProgressBar) при добавлении других компонентов в JPanel.

Эта операция тяжелая и занимает около 2 секунд с 20 итерациями (добавление 20 элементов). Но может быть и 100 штук.

Поэтому мне нужно добиться отображения текущего состояния каждой итерации в JProgressBar, но я не могу понять, как это сделать. (В моем коде это значение perc)

И можете ли вы объяснить, как работает EDT? Добавляет ли события в очередь в конец? Спасибо!

Мой объект JProgressBar: categoryStatus.

Вот мой код:

    categoryStatus = new LayoutProgressBar(150, 200, 800, 20, Color.decode("#F7F7F7"), 3, 0);
    workPanel.add(categoryStatus);

    LayoutPanel modsPanel = new LayoutPanel( 5, 64, 1090, 448, new Color(0,0,0,0));
    modsPanel.setLayout(new BorderLayout());

    LayoutPanel subModPanel = new LayoutPanel(8, 50, modsPanel.getWidth()-16, 300, new Color(0,0,0,0));
    subModPanel.setPreferredSize(new Dimension(modsPanel.getWidth()-16, mods.size()*172 ));

    Thread thrd1 = new Thread() {
        public void run() {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    @Override
                    public void run() {
                        long startTime = System.nanoTime();
                        for(int i = 0; i<mods.size(); i++){
                            String mod_url = mods.get("mod_"+i).get("mod_url").toString();
                            String title = mods.get("mod_"+i).get("name").toString();
                            String mod_type = mods.get("mod_"+i).get("type").toString();

                            LayoutPanel curModPanel = new LayoutPanel(10, i*172+5, 1060, 156, new Color(0,0,0,55));

                            LayoutLabel last_upd = new LayoutLabel(185, 112, 17, 17, true, false, Color.black, new ImageIcon(LoadingComp.class.getResource("/images/upd.png")), "", 12, "MullerBold.otf");
                            LayoutLabel vers_icon = new LayoutLabel(355, 112, 19, 19, true, false, Color.black, new ImageIcon(LoadingComp.class.getResource("/images/cube.png")), "", 12, "MullerBold.otf");
                            LayoutLabel vers = new LayoutLabel(385, 115, 130, 14, false, false, Color.decode("#00B9FF"), null, mods.get("mod_"+i).get("version").toString(), 12, "MullerMedium.otf");   
                            LayoutLabel last_date = new LayoutLabel(215, 115, 130, 14, false, false, Color.decode("#00B9FF"), null, mods.get("mod_"+i).get("date").toString(), 12, "MullerMedium.otf"); 
                            LayoutLabel titleLabel = new LayoutLabel(185, 15, 130, 14, false, false, Color.white, null, title, 14, "MullerBold.otf");   
                            LayoutLabel author1 = new LayoutLabel(185, 40, 130, 14, false, false, Color.decode("#8790a6"), null, "Автор:", 12, "MullerMedium.otf"); 
                            LayoutLabel author2 = new LayoutLabel(228, 40, 130, 14, false, false, Color.decode("#00B9FF"), null, mods.get("mod_"+i).get("author").toString(), 12, "MullerMedium.otf");  
                            LayoutTextPane descr = new LayoutTextPane(185, 65, curModPanel.getWidth()-200, 40, "Roboto-Regular.ttf", mods.get("mod_"+i).get("descr").toString(), 12, Color.decode("#D3D4E4"), null, false, StyleConstants.ALIGN_LEFT);

                            String mod_action = "Установить";
                            String mod_btn = "install_btn";
                            if(lut.checkFileExistence( lut.setModDestination(mod_type, title) )) {
                                mod_action = "Удалить";
                                mod_btn = "delete_btn";
                            }

                            LayoutButton mod_btn_status = new LayoutButton(18, 103, 135, 34, false, mod_action, Color.WHITE, mod_btn, 14, "MullerBold.otf");
                            mod_btn_status.addActionListener(new ActionListener(){

                                @Override
                                public void actionPerformed(ActionEvent arg0) {
                                    workWithMod(mod_btn_status, curModPanel, title, mod_url, mod_type);

                                    workPanel.revalidate();
                                    workPanel.repaint();
                                }

                            });

                            LayoutLabel img = new LayoutLabel(18, 15, 136, 72, true, false, Color.black, new ImageIcon(DefaultUtils.getWebImage(mods.get("mod_"+i).get("img_url").toString())), "", 12, "MullerBold.otf");

                            curModPanel.add(mod_btn_status);
                            curModPanel.add(last_upd);
                            curModPanel.add(vers_icon);
                            curModPanel.add(last_date);
                            curModPanel.add(img);
                            curModPanel.add(vers);
                            curModPanel.add(titleLabel);
                            curModPanel.add(author1);
                            curModPanel.add(author2);
                            curModPanel.add(descr);

                            subModPanel.add(curModPanel);

                            int perc = i * 100 / mods.size();   
                            //new Worker(perc, categoryStatus).execute();
                        }       
                    }

                });
            } catch (InvocationTargetException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    thrd1.start();

    categoryStatus.setValue(50);

    Thread thrd2 = new Thread() {
        public void run() {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    @Override
                    public void run() {
                        scrollPane = new JScrollPane(subModPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
                        scrollPane.setOpaque(false);
                        scrollPane.setViewportView(subModPanel);
                        scrollPane.getViewport().putClientProperty("EnableWindowBlit", Boolean.TRUE);
                        scrollPane.getViewport().setOpaque(false);
                        scrollPane.setBorder(BorderFactory.createEmptyBorder());
                        scrollPane.getVerticalScrollBar().setOpaque(false);
                        scrollPane.getVerticalScrollBar().setUnitIncrement(5);
                        scrollPane.getVerticalScrollBar().setUI(new LayoutScrollPane(Color.white));
                        scrollPane.getVerticalScrollBar().setPreferredSize(
                                new Dimension(7, Integer.MAX_VALUE));

                        modsPanel.add(scrollPane);

                        workPanel.remove(categoryStatus);
                        workPanel.repaint();
                        workPanel.revalidate();

                        workPanel.add(modsPanel);
                    }

                });

                SwingUtilities.invokeAndWait(new Runnable() {
                    @Override
                    public void run() {
                        scrollPane.getVerticalScrollBar().setValue(0);
                    }
                });
            } catch (InvocationTargetException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

    thrd2.start();

person Shellai    schedule 23.09.2019    source источник
comment
Не обращайте внимания на мой рефакторинг кода :) Я просто пробовал много способов, но так и не нашел хорошего решения.   -  person Shellai    schedule 24.09.2019
comment
Ваш код не может быть скомпилирован таким образом. Предоставьте минимально воспроизводимый пример, чтобы проиллюстрировать проблему. И минимум будет ‹ 100 строк.   -  person WJS    schedule 24.09.2019
comment
Извините, но я не могу больше минимизировать свой код, потому что это средний проект Java со многими классами. Вот только моя проблемная зона.   -  person Shellai    schedule 24.09.2019
comment
Не можете ли вы воссоздать проблему, с которой вы столкнулись, только с этим разделом? Или предоставьте очень подробное описание того, какие проблемы у вас возникли.   -  person WJS    schedule 24.09.2019
comment
Я добавил JProgressBar, и мое приложение успешно его отобразило (состояние категории LayoutProgressBar). LayoutProgressBar — это всего лишь класс, расширяющий JProgressBar пользовательской прорисовкой. Таким образом, проблема заключается в том, чтобы обновить статус этого JProgressBar внутри моего цикла, добавляя множество компонентов SWING. Вам не нужно компилировать этот код, просто дайте мне пример, как решить мою проблему. Я не могу найти причину, почему это не работает.   -  person Shellai    schedule 24.09.2019
comment
Я думаю, вам было бы полезно научиться пользоваться SwingWorker   -  person MadProgrammer    schedule 24.09.2019
comment
Я могу сказать, что мой вопрос звучит так: Как отобразить статус предварительной загрузки в графическом интерфейсе при добавлении множества компонентов Swing :) Пример, когда я добавляю 100 JLabels, одновременно показываю JProgressBar и отображаю значение от 1 до 100 (рабочий статус) .   -  person Shellai    schedule 24.09.2019
comment
Взгляните на stackoverflow.com/questions/58020090/   -  person George Z.    schedule 24.09.2019


Ответы (1)


Я написал это, чтобы продемонстрировать, что, по моему мнению, вы пытаетесь сделать. Он создает две основные панели. На верхнем находится ProgressBar. Нижний содержит меньшие панели, созданные в потоке. Таймер Swing используется для имитации активности при добавлении небольших панелей.


    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.lang.reflect.InvocationTargetException;

    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JProgressBar;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;

    public class ProgressDemo extends JPanel {
       JProgressBar bar        = new JProgressBar();
       JFrame       frame      = new JFrame();
       int          n          = 0;
       int          panelCount = 0;
       JPanel       subPanel   = new JPanel();

       public ProgressDemo() {
          setPreferredSize(new Dimension(500, 500));
          subPanel.setPreferredSize(new Dimension(500, 450));
          frame.add(this);
          add(bar);
          add(subPanel);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
          Timer timer = new Timer(0, new MyActionListener());
          timer.setDelay(200);
          bar.setMaximum(200);

          timer.start();

       }
       public static void main(String[] args) {
          new ProgressDemo().start();
       }
       public void start() {
          for (int i = 0; i < 200; i++) {
             try {
                SwingUtilities.invokeAndWait(() ->
                {
                   subPanel.add(new MyPanel());
                   revalidate();
                   frame.repaint();
                });
                 sleep(500);
             }
             catch (InvocationTargetException | InterruptedException ite) {
                ite.printStackTrace();
             }
          }
       }

       public static void sleep(int milli) {
          try {
             Thread.sleep(milli);
          }
          catch (InterruptedException ie) {
          }
       }

       private class MyActionListener implements ActionListener {
          public void actionPerformed(ActionEvent ae) {
             n++;
             if (n > 200) {
                  n = 0;
             }
             bar.setValue(n);
          }
       }

       Color[] colors = {
             Color.red, Color.blue, Color.green, Color.yellow, Color.cyan,
             Color.magenta
       };

       class MyPanel extends JPanel {
          public MyPanel() {
             setBackground(colors[panelCount % colors.length]);
             int w = 25;
             int h = 25;
             setPreferredSize(new Dimension(w, h));
             panelCount++;
             setVisible(true);
          }

       }
    }

person WJS    schedule 23.09.2019
comment
Да, это похоже на решение моей проблемы. Но могу ли я добиться этого каким-то другим способом? Мне нужно сначала создать и отобразить свой JProgressBar, а затем в другом потоке или по таймеру добавить элементы один за другим и одновременно обновить мой JProgressBar. Когда задача будет завершена, просто удалите мой JProgressBar и отобразите сгенерированную JPanel с элементами. Является ли это возможным? - person Shellai; 24.09.2019
comment
Да, пока вы можете определить, когда вы закончили, вы можете просто делать то, что предложили. Кроме того, в моем примере мне не нужно было использовать поток. Вы можете удалить его и посмотреть сами. Вы уверены, что они вам действительно нужны? - person WJS; 24.09.2019
comment
Хорошо, я попробую. Я думаю, что нет необходимости создавать другие темы, если моя проблема будет решена этим кодом :) - person Shellai; 24.09.2019
comment
Большое тебе спасибо! Я интегрировал ваш код в свой проект, и он отлично работает! Проблема решена :) - person Shellai; 24.09.2019