Показване на неопределен JProgressBar, докато пакетният файл се изпълнява

От известно време разглеждам SO и Google за отговор на този въпрос, но не мога да намеря такъв, който наистина работи. Ще започна отначало:

Създадох Java клас с метод, който изпълнява пакетен файл във фонов режим (командният прозорец не се появява). Програмата работи чудесно, с изключение на това, че би било малко объркващо за крайния потребител, тъй като пакетният файл отнема известно време, за да завърши - потребителят няма да знае дали програмата все още работи или не. След като груповият скрипт завърши изпълнението, се появява диалогов прозорец със съобщение, че е завършен, но за периода от време между момента, в който методът се изпълнява и диалоговият прозорец се появява, изглежда, че програмата не прави нищо.

И така, ето моят въпрос: много бих искал да покажа нов кадър с текстова област, която показва изхода на пакетния файл. Разбирам обаче, че това е много трудно да се направи без създаване на временни файлове, писане в тях, четене от тях и т.н. Предпочитам да го избегна, ако е възможно. Затова реших, че може да е по-добре да показвам неопределен JProgressBar, докато процесът работи, и да го затварям, когато процесът приключи. За съжаление, не мисля, че Swing може да се справи с това, тъй като ще изисква стартиране на множество процеси наведнъж. Чувал съм за SwingWorker, но не съм съвсем сигурен как ще се използва в този случай. Имам следния SSCCE, който работи, но няма внедрена лента за напредъка.

public myClass(){
    public static void main(String[] args){
        String[] commands = {"cmd.exe", "/C", "C:\\users\\....\\myBat.bat"};
        Process p = Runtime.getRuntime().exec(commands);
        p.waitFor()
        JOptionPane.showMessageDialog(null, "Process finished!");
    }
}

Докато p.waitFor() чака процеса, на екрана няма нищо. Просто искам нещо, което да показва на потребителя, че даден процес все още работи. мисли? Благодаря!


person DerStrom8    schedule 15.12.2013    source източник


Отговори (1)


Можете да стартирате ProcessBuilder във фонов режим на SwingWorker, както е показано по-долу, за да получите и двете изходни данни и лента за напредък.

изображение

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.*;

/**
 * @se http://stackoverflow.com/a/20603012/230513
 * @see http://stackoverflow.com/a/17763395/230513
 */
public class SwingWorkerExample {

    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER);
    private final JTextArea textArea = new JTextArea(20, 20);
    private JButton startButton = new JButton("Start");
    private JButton stopButton = new JButton("Stop");
    private JProgressBar bar = new JProgressBar();
    private BackgroundTask backgroundTask;
    private final ActionListener buttonActions = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            JButton source = (JButton) ae.getSource();
            if (source == startButton) {
                textArea.setText(null);
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
                backgroundTask = new BackgroundTask();
                backgroundTask.execute();
                bar.setIndeterminate(true);
            } else if (source == stopButton) {
                backgroundTask.cancel(true);
                backgroundTask.done();
            }
        }
    };

    private void displayGUI() {
        JFrame frame = new JFrame("Swing Worker Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel panel = new JPanel();
        panel.setBorder(
            BorderFactory.createEmptyBorder(5, 5, 5, 5));
        panel.setLayout(new BorderLayout(5, 5));

        JScrollPane sp = new JScrollPane();
        sp.setBorder(BorderFactory.createTitledBorder("Output: "));
        sp.setViewportView(textArea);

        startButton.addActionListener(buttonActions);
        stopButton.setEnabled(false);
        stopButton.addActionListener(buttonActions);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(startButton);
        buttonPanel.add(stopButton);
        buttonPanel.add(bar);

        panel.add(statusLabel, BorderLayout.PAGE_START);
        panel.add(sp, BorderLayout.CENTER);
        panel.add(buttonPanel, BorderLayout.PAGE_END);

        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private class BackgroundTask extends SwingWorker<Integer, String> {

        private int status;

        public BackgroundTask() {
            statusLabel.setText((this.getState()).toString());
        }

        @Override
        protected Integer doInBackground() {
            try {
                ProcessBuilder pb = new ProcessBuilder("ls", "-lR", "/");
                pb.redirectErrorStream(true);
                Process p = pb.start();
                String s;
                BufferedReader stdout = new BufferedReader(
                    new InputStreamReader(p.getInputStream()));
                while ((s = stdout.readLine()) != null && !isCancelled()) {
                    publish(s);
                }
                if (!isCancelled()) {
                    status = p.waitFor();
                }
                p.getInputStream().close();
                p.getOutputStream().close();
                p.getErrorStream().close();
                p.destroy();
            } catch (IOException | InterruptedException ex) {
                ex.printStackTrace(System.err);
            }
            return status;
        }

        @Override
        protected void process(java.util.List<String> messages) {
            statusLabel.setText((this.getState()).toString());
            for (String message : messages) {
                textArea.append(message + "\n");
            }
        }

        @Override
        protected void done() {
            statusLabel.setText((this.getState()).toString() + " " + status);
            stopButton.setEnabled(false);
            startButton.setEnabled(true);
            bar.setIndeterminate(false);
        }

    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new SwingWorkerExample().displayGUI();
            }
        });
    }
}
person trashgod    schedule 16.12.2013
comment
Това е фантастично! Ще трябва да поработя малко с него, за да отговаря на нуждите ми. Ще трябва да бъде отделен клас и да предавам моите команди за процес в него. В момента имам променлива на класа и метод на мутатор за задаване на стойността. По някаква причина обаче изглежда не изпълнява партидния файл. Ще направя още малко отстраняване на грешки и ще се свържа с вас. - person DerStrom8; 16.12.2013
comment
Работи според очакванията, благодаря ви много! За съжаление работи много бавно. Предполагам, че няма начин да се ускори? - person DerStrom8; 16.12.2013
comment
Предполагам, че това е пакетният файл; Трябваше да го забавя, за да тествам; профил, за да сте сигурни. - person trashgod; 16.12.2013
comment
Преди добавянето на този клас партидният файл ще стартира и ще завърши в рамките на около 30 секунди. Сега продължава повече от час (може би два?) и все още не е завършено. - person DerStrom8; 16.12.2013
comment
Трудно е да се каже: партида чака въвеждане? твърде много текст? няма прекъсвания на редове? работи ли с dir /s /b? - person trashgod; 16.12.2013
comment
Съжалявам, трябваше да спомена, че използвам собствен пакетен файл, който написах, за да копирам файлове от едно устройство на друго. Конструкторът на процеси използва низов масив, наречен commands, който включва всички аргументи и команди, които се използват за извикване/изпълнение на прилепа. Все още не съм сигурен защо отнема толкова време и дори не знам откъде да започна да търся. - person DerStrom8; 16.12.2013
comment
Не съм напълно запознат с класа SwingWorker, но прочетох, че стартирането на външен процес от EDT ще го накара да бъде много бавен или дори да спре да реагира. Това изглежда описва проблема ми перфектно. Предполагам, че това не се случва тук? - person DerStrom8; 18.12.2013
comment
точно; идеята е да стартирате бавния процес в doInBackground, фоновата нишка на работника и process резултатите на EDT. Вашата партида завършва ли, макар и бавно, или остава блокирана за неопределено време? - person trashgod; 18.12.2013
comment
В крайна сметка той завършва, но процес, който отнема 30 секунди, обикновено отнема повече от два часа, когато се изпълнява с помощта на класа SwingWorker. Направих няколко редакции в него, но това беше само за промяна на оформлението на потребителския интерфейс. Не би трябвало да причинява големи грешки като този - person DerStrom8; 18.12.2013
comment
Вашата партида произвежда ли достатъчно изход, за да задуши текстовата област или да причини размяна? Какво показва профилирането или диспечера на задачите? Не мога да възпроизведа ефекта, който описвате, но можете да опитате да копирате файловете, като използвате един от подходите, показани тук. - person trashgod; 18.12.2013
comment
Моят пакетен файл се състои предимно от цикъл, съдържащ командата xcopy. Директориите, които копирам, се предават с помощта на масива от команди, който е в моя код, който публикувах в оригиналната публикация. Резултатът от груповия файл е просто ред, показващ директорията, която се копира. Не съм сигурен дали това задушава текстовата област или не. Диспечерът на задачите показва само java процес и cmd процес (свързан с този проект). Не съм съвсем сигурен как да направя профилирането, което предлагате, честно казано, това е нов термин за мен. Все пак ще проверя линка ти. Много благодаря за цялата ви помощ, между другото! - person DerStrom8; 18.12.2013
comment
Виждате ли резултата от всеки xcopy? Докато приложението ви работи, стартирайте jvisualvm и го прикачете, за да видите какво се случва. Вижте също този отговор на xcopy или опитайте Files.copy. - person trashgod; 18.12.2013
comment
Виждам изхода от xcopy, да. Не стартирам програмите си в командния ред, обикновено използвам Eclipse. Има ли инструмент, който мога да използвам в Eclipse вместо това? Ако не мога да накарам това да работи, може да се наложи да преосмисля потока на цялата си програма и да използвам Files.copy вместо пакетни файлове на Windows - person DerStrom8; 18.12.2013
comment
Мразя да разширявам тази нишка за коментари, просто исках да спомена, че в момента копирането на 2 MB файлове отнема 2-3 минути. Стартирайки направо от партидния файл, това отнема около 3-5 секунди. - person DerStrom8; 18.12.2013
comment
Откакто коментирах последно, забелязах, че когато се натисне бутонът за спиране, той не затваря командния процес. Той остава отворен в диспечера на задачите. Предположих, че това е, което p.destroy() трябваше да направи - да го затвори - но изглежда не го прави. Някакви идеи? - person DerStrom8; 21.12.2013
comment
Това се очаква; хост ОС притежава нишката, която трябва да е в състояние на паркиране/изчакване; мениджърът на задачите е доста груб изглед; профил, за да видите повече подробности. - person trashgod; 21.12.2013
comment
Значи трябва да остави процесите отворени след завършване? Това изглежда като малко количество ресурс и компютърът ми почти се срина няколко пъти поради замразяване на Windows Explorer. - person DerStrom8; 21.12.2013
comment
Виждам 48 байта на нефункциониращ работник, които изчезват, когато JVM излезе. - person trashgod; 21.12.2013
comment
Странно. Моят продължи да работи дори след излизане от JVM и в един момент дори продължи да копира файлове дори при натискане на бутона за спиране. Предполагам, че ще продължа да се занимавам с това и ще видя какво мога да измисля. Благодаря ви много, много ми помогнахте - person DerStrom8; 21.12.2013