Sementara kode saya mengirim panggilan http, saya ingin mengubah JLabel di jendela (pada dasarnya menunjukkan kira-kira berapa banyak waktu yang tersisa sebelum panggilan http berakhir). Saya telah memeriksa SwingWorkers karena pertanyaan lain yang saya ajukan di sini, tetapi saya tidak yakin bagaimana saya menggunakannya. Kode yang saya tulis pada dasarnya memiliki loop untuk mengirim panggilan, setiap kali menghitung waktu yang dibutuhkan untuk menjalankan panggilan, menghitung perkiraan waktu yang tersisa dan kemudian mengirimkannya ke JLabel (NB JLabel berada di tempat yang berbeda objek yang diinstansiasi).

Sebagian besar contoh SwingWorker menunjukkan fungsi yang berlanjut di latar belakang yang tidak terpengaruh oleh utas pekerja (mis. penghitung yang sepenuhnya didasarkan pada waktu daripada diubah oleh kode). Jika ini masalahnya, bukankah perubahan JLabel hanyalah bagian dari utas pekerja karena kode berjalan melalui loop baru -> hitung waktu & lakukan panggilan -> ubah JLabel? Saya mungkin salah, tetapi bagaimana saya mengubah JLabel dengan kode daripada utas independen?

Salah satu masalah saya adalah ketika saya pertama kali mengatur kode saya, tidak ada yang berubah di JLabel.

Ini kode saya:

package transcription.windows;

import javax.swing.*;

import java.awt.*;

import static javax.swing.JFrame.EXIT_ON_CLOSE;

public class PleaseWaitWindow {

    private JLabel pleaseWaitJLabel = new JLabel("Please wait");
    private GridBagConstraints containerGbc = new GridBagConstraints();
    private Container contentPaneContainer = new Container();
    private JFrame pleaseWaitJFrame;

    public JLabel getPleaseWaitJLabel() {
        return pleaseWaitJLabel;
    }

    public JFrame setPleaseWaitWindow() {
        pleaseWaitJFrame = new JFrame();
        contentPaneContainer = setContentPane();
        pleaseWaitJFrame.setContentPane(contentPaneContainer);
        pleaseWaitJFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        pleaseWaitJFrame.setTitle("");
        pleaseWaitJFrame.setSize(350, 150);
        pleaseWaitJFrame.setLocationRelativeTo(null);
        pleaseWaitJFrame.setVisible(true);

        return pleaseWaitJFrame;
    }

    private Container setContentPane() {
        containerGbc.insets.bottom = 1;
        containerGbc.insets.top = 2;
        containerGbc.insets.right = 1;
        containerGbc.insets.left = 1;
        containerGbc.weightx = 1;
        containerGbc.weighty = 1;

        contentPaneContainer.setLayout(new GridBagLayout());
        contentPaneContainer.setSize(800, 700);
        setPleaseWaitJLabel();

        return contentPaneContainer;
    }

    private void setPleaseWaitJLabel() {
        containerGbc.gridx = 2;
        containerGbc.gridy = 5;
        containerGbc.gridwidth = 2;
        containerGbc.gridheight = 1;
        contentPaneContainer.add(pleaseWaitJLabel, containerGbc);
    }

    public void setJLabelDisplay(String displayTime) {
        pleaseWaitJLabel.setText(displayTime);
    }

    public void closeWindow() {
        pleaseWaitJFrame.dispose();
    }
    }

Metode yang merupakan bagian dari kelas ServiceUpload:

    public String cuttingLoop(String mpBase64Piece, String jobName, String email) {
        Integer numberOfPiecesMinusEnd = (int) Math.ceil(mpBase64Piece.length() / 500000.0);
        List<String> base64List = new ArrayList<>();

        for (int i = 0; i < numberOfPiecesMinusEnd; i++) {

            if (mpBase64Piece.length() >= 500000) {
                base64List.add(mpBase64Piece.substring(0, 500000));
                mpBase64Piece = mpBase64Piece.substring(500000);
            }

        }
        base64List.add(mpBase64Piece);

        pleaseWaitWindow = new PleaseWaitWindow();
        pleaseWaitWindow.setPleaseWaitWindow();
        for (int n = 0; n < base64List.size(); n++) {
            numberOfLoopsLeft = numberOfPiecesMinusEnd - n;
            Stopwatch stopwatch = null;
            String tag;
            Stopwatch.createStarted();
            if (base64List.get(n) != null) {
                if (n == 0) {
                    tag = "start";
                } else if (n == base64List.size() - 1) {
                    tag = "end";
                } else {
                    tag = "middle";
                }
                stopwatch = Stopwatch.createStarted();
                response = providerUpload.executeUploadHttp(base64List.get(n), jobName, tag, email);
                stopwatch.stop();
            }
            long oneLoopTime = stopwatch.elapsed(TimeUnit.SECONDS);
            pleaseWaitWindow.setJLabelDisplay(numberOfLoopsLeft*oneLoopTime+" seconds remaining");
            LOGGER.info("complete");
        }
        pleaseWaitWindow.closeWindow();
        return response;
    }

Salah satu masalah saya adalah kode tidak menunjukkan 'JLabel' ketika 'SwingWorker' tidak digunakan dengan kode di atas.

0
Hywel Griffiths 9 Juli 2020, 08:47

1 menjawab

Jawaban Terbaik

Sebaiknya Anda membagi kode Anda ke dalam area tanggung jawab. Mari kita pergi dengan tiga: 1. pekerja (yaitu unggahan); 2. tampilan (yaitu pembaruan JLabel); 3. integrasi keduanya (dua yang pertama independen satu sama lain, jadi Anda memerlukan sesuatu untuk menyatukannya).

Mengabstraksi dari pekerjaan sebenarnya, Anda dapat menggunakan antarmuka standar. Yang pertama hanyalah Runnable, yaitu tidak mengambil parameter apa pun dan tidak mengembalikan apa pun. Yang kedua adalah Consumer<String> karena membutuhkan String (untuk ditampilkan) tetapi tidak mengembalikan apa pun. Yang ketiga akan menjadi kontrol utama Anda.

Mari kita mulai dengan kontrol karena itu sederhana:

Consumer<String> display = createDisplay();
Runnable worker = createWorker();
CompletableFuture.runAsync(worker);

Ini akan memulai pekerja di Utas terpisah yang sepertinya Anda inginkan.

Jadi, inilah pengunggah Anda:

Consumer<String> display = // tbd, see below
Runnable worker = () -> {
    String[] progress = {"start", "middle", "finish"};
    for (String pr : progress) {
        display.accept(pr);
        Thread.sleep(1000); // insert your code here
    }
}

Perhatikan bahwa pekerja ini sebenarnya bergantung pada konsumen; itu agak "najis", tetapi akan dilakukan.

Sekarang untuk tampilan. Setelah mendefinisikannya sebagai Consumer<String>, itu cukup abstrak sehingga kita bisa mencetak progresnya di konsol.

Consumer<String> display = s -> System.out.printf("upload status: %s%n", s);

Namun Anda ingin memperbarui JLabel; sehingga konsumen akan terlihat seperti

Consumer<String> display = s -> label.setText(s);
// for your code
s -> pleaseWaitWindow.getPleaseWaitLabel().setText(s);

Pertanyaan Anda yang sebenarnya

Jadi jika Anda melakukannya, Anda akan melihat bahwa teks label Anda tidak diperbarui seperti yang Anda harapkan. Itu karena label.setText(s) dieksekusi di utas di mana pekerja sedang berjalan; itu perlu dimasukkan ke dalam utas Swing. Di situlah SwingWorker masuk.

SwingWorker memiliki bidang progress yang dapat Anda gunakan untuk label Anda; itu juga memiliki doInBackground() yang merupakan utas pekerja unggahan Anda yang sebenarnya. Jadi Anda berakhir dengan

class UploadSwingWorker {
    public void doInBackground() {
        for(int i = 0; i < 3; i++) {
            setProgress(i);
            Thread.sleep(1000); // again, your upload code
        }
    }
}

Jadi bagaimana cara memperbarui label Anda? setProgress memunculkan PropertyChangeEvent yang dapat Anda cegat; ini dilakukan menggunakan PropertyChangeListener dengan tanda tangan

void propertyChange(PropertyChangeEvent e)

Ini adalah antarmuka fungsional, sehingga Anda dapat mengimplementasikannya dengan lambda, dalam kasus Anda

String[] displays = {"start", "middle", "finish"};
updateLabelListener = e -> {
    int index = ((Integer) e.getNewValue()).intValue(); // the progress you set
    String value = displays[index];
    label.setText(value);
}

Dan Anda dapat menambahkannya ke SwingWorker menggunakan

SwingWorker uploadWorker = new UploadSwingWorker();
uploadWorker.addPropertyChangeListener(updateLabelListener);
uploadWorker.execute(); // actually start the worker

Sederhana

Perhatikan bahwa saya sendiri tidak pernah menggunakan SwingWorker dengan cara ini. Cara yang lebih sederhana untuk mengatasi masalah bahwa GUI tidak diperbarui dari dalam utas pekerja Anda adalah dengan memanggil pembaruan GUI menggunakan SwingUtilities.invokeLater().

Kembali ke awal Consumer<String> yang saya kemukakan, Anda bisa melakukannya

    Consumer<String> display = s -> SwingUtilities.invokeLater(
        () -> pleaseWaitWindow.getPleaseWaitLabel().setText(s)
    );

Dan itu harus dilakukan. Hal ini memungkinkan Anda untuk menjaga pekerja Anda dalam Runnable yang lebih abstrak dan menggunakan mekanisme penjadwalan biasa untuk menjalankannya (ExecutorService.submit() atau CompletableFuture.runAsync() misalnya), sambil tetap memungkinkan untuk memperbarui GUI pada tingkat yang sama sederhana.

1
daniu 10 Juli 2020, 09:52