スクレイピングされたデータでさまざまな操作を実行する Web スクレイピング ツールに取り組んでいます。
このため、さまざまな異なる GUI が整然と動作する必要があり、そのため、メイン メソッドがそれぞれの目的を完了する前に待機する必要があります。
しばらく検索した後、問題を解決する方法の手がかりを提供する次の StackOverflow の質問を見つけましたが、私のケースとはいくつかの違いがあるため、実装できませんでした。
テキスト フィールドへの入力を待機する 方法 メイン スレッドが別のスレッドの終了を待機するようにする方法
GUI のコンポーネント (ボタンなど) のリスナーを使用してコードをトリガーできることはわかっていますが、そのリスナーが起動するまでメインスレッドを待機させるのに苦労しています。 GUI のスレッド (存在する場合) は、メイン スレッドによって初期化されます...
これは、プログラムがどのように機能するかを示す単純化されたコードです。
public class Main {
/*
* Waiter is a simple GUI with just an "Start" button in it. Here in place of my actual GUIs.
*/
private static Waiter auth; //Represents my NTLM-authentication form.
private static Waiter status; //Represents a status-feedback GUI that will be displayed during processing.
private static Waiter operation; //Represents a GUI in with the user choses what to do with the gathered data.
public static void main(String[] args) throws InterruptedException {
auth = new Waiter();
auth.setVisible(true);
System.out.println("NTLM Authentication form. Should wait here until user has filled up the GUI and clicked \"Start\".");
System.out.println("Authenticates WebClient's NTLM using data inputed to the GUI...");
auth.dispose();
Thread srt = new Thread(status = new Waiter());
srt.start();
status.setVisible(true);
//Performs webscraping operations...
System.out.println("Prepares the webscraped data here...Things like downloading files and/or parsing text...");
System.out.println("Keeps the user aware of the progress using the \"status\" GUI.");
status.setVisible(false);
//Clears the status GUI.
operation = new Waiter();
operation.setVisible(true);
System.out.println("Operation selection form. Should wait here until user selects an option.");
System.out.println("Starts performing the operation(s)...");
operation.dispose();
status.setVisible(true);
System.out.println("Performs the operation(s), while giving status-feedback to the user.");
status.setVisible(false);
System.out.println("Displays a file-save dialog to save the results.");
System.out.println("And finally, displays a \"End of operations\" dialog before ending.");
}
}
更新 1: 私が抱えている主な問題は、次のようなものを実装することです (これが私がやりたいことです):
//Main method...code...
Thread srt = new Thread(status = new Waiter());
//Before "srt.start();"...
status.startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
main.continueExecution();
}
});
//Thread's run() being something like "status.setVisible(true); main.waitGUI();"
srt.start();
//continues here after the Listener is triggered...more code...
これの代わりに(私がそれを正しく理解している場合、他のほとんどの人にとって解決策は何ですか...)(これは、可能であれば私がやりたくないことです):
//GUI before this one...
//code...
Thread srt = new Thread(status = new Waiter());
status.startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
/*
* Code that should come after this GUI.
*/
}
});
//Thread's run() being something like "status.setVisible(true);"
srt.start();
//"ends" here...(Initial code or GUI before this "status")
言い換えれば、実際の処理コードをトリガーする代わりに、メインのスレッドの「スリープ」および「ウェイクアップ」アクションをトリガーする方法で GUI とリスナーを実装するのに問題があります。
更新 2:
@JB_Nizet のヒントに従ってSwingUtilities.invokeLater()
、SwingUtilities docsをよく見て、メソッドがどのように機能するかを知った後、とSwingUtilities.invokeAndWait()
の組み合わせを使用してそれを行う方法を見つけたと思います。Semaphore
invokeAndWait()
それが安全で有効な解決策であるかどうかを確認するために、マルチスレッドや GUI をよりよく理解している人が必要です。(その後、質問を編集してクリーンアップし、確認された場合は、これを適切な「回答形式」で投稿します)
とにかく、ここに私のために働いているように見える修正されたコードがあります:
public class Main_Test {
//Semaphore:
public static Semaphore semaphore;
//GUIs:
private static Waiter auth; //Represents my NTLM-authentication form.
public static void main(String[] args) {
try {
semaphore = new Semaphore(1);
// semaphore.acquire();
auth = new Waiter() {
@Override
public void run() {
try {
System.out.println(Main_Test.getThread() + this.getName() + " has been created and is now running.");
semaphore.acquire(); //Makes main pause.
this.setVisible(true);
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
auth.jButton1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(getThread() + "NTLM has been hypothetically authenticated.");
semaphore.release(); //Makes main continue after GUI is done.
auth.dispose();
}
});
// semaphore.release();
SwingUtilities.invokeAndWait(auth);
semaphore.acquire(); //<- Where the main effectively gets paused until the permit is released.
/*
* GUI's run() will accquire the semaphore's permit.
* The invokeAndWait() garantees (?) it will happen before main's acquire().
* This causes the main to pause when trying to acquire the permit.
* It stays paused until the actionListener release() that permit.
*/
System.out.println(getThread() + "This message represents the processing, and should come only after the hypothetical NTLM authentication.");
} catch (InterruptedException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(Main_Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static String getThread() {
return String.format("%-32s --- ", Thread.currentThread().toString());
}
}