- PR -

ウインドウが表示されません。

1
投稿者投稿内容
you5
会議室デビュー日: 2007/04/21
投稿数: 4
投稿日時: 2007-04-21 01:05
Java 初心者です。eclipse+VisualEditorで勉強しています。

以下のようなテストクラスを作って、別のクラス(main)からインスタンスを発生させて、
ウインドウを表示させようとしているのですが、どうもうまくいきません。

mainの方では、フレーム(JFrame)の派生クラスを作成し、"実行"というメニューアイテムを追加して、アクションリスナーで実行メニューが選択された場合に、インスタンスを発生させて、計算メソッドを実行するようにしています。

jMenuItem5.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
TestClass tc =new TestClass();
tc.calrun();
}
});

インスタンスを発生させるだけで、そのまま終了させる(閉じない)場合には、
ちゃんと表示されたまま残るのですが、
tc.calrun();
がある場合には、ウインドウの枠は表示されるのですが、ボタンを貼り付けた
中身が見えません。背後が透過した状態になります。

何か問題があるでしょうか?
※ 説明を簡略化するために、単純なプログラムに書き換えましたが、本当はcalrun()は数値シミュレーションの長い計算を行い、同時にプログレスバーで計算進捗状況を表示しようとし考えています。
また、VisuslEditorで自動生成しているので、十分理解していないコードが
多々あります。これから勉強します。どうぞよろしくお願いします。

TestClass.javaは以下の通りです。

import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import javax.swing.JCheckBox;
import javax.swing.JScrollBar;
import javax.swing.SwingConstants;

public class TestClass extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel jContentPane = null;
private JButton jButton = null;

public TestClass() {
super();
initialize();
}

private void initialize() {
this.setSize(300, 200);
this.setContentPane(getJContentPane());
this.setTitle("JFrame");
this.setVisible(true);
}

private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
jContentPane.add(getJButton(), BorderLayout.CENTER);
}
return jContentPane;
}

private JButton getJButton() {
if (jButton == null) {
jButton = new JButton();
jButton.setPreferredSize(new Dimension(10, 20));
jButton.setMnemonic(KeyEvent.VK_UNDEFINED);
jButton.setHorizontalAlignment(SwingConstants.LEFT);
jButton.setText("test");
}
return jButton;
}

public void calrun(){

for( long k=0; k <= 100; k++ ){
 if(this.isVisible()){
  this.setVisible(false);
 }else{
  this.setVisible(true);
 }

     try {
Thread.sleep(100);
 } catch (InterruptedException e) {
e.printStackTrace();
 }

}
  return;
 }
}
大ベテラン
会議室デビュー日: 2006/06/28
投稿数: 116
投稿日時: 2007-04-21 11:59
問題を発生させているcalrunメソッドを確認すると、
コード:
public void calrun(){
	for( long k=0; k <= 100; k++ ){
		if(this.isVisible()){
			this.setVisible(false);
		}else{
			this.setVisible(true);
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	return;
}


JFrameの表示と非表示を短い間隔で行きつ戻りつします。
このため、内部のコンポーネントの再描画が追いついていないのでしょう。
フレームの表示を制御せず、forループの外で、常に表示するようにしておけばよいのでは?
you5
会議室デビュー日: 2007/04/21
投稿数: 4
投稿日時: 2007-04-21 23:26
ご回答ありがとうございます。

確かに、いまのcalrunはテストのためにそのようにしましたが、
(かえって、混乱するコードで申し訳ありません。あまり重要なところではありません。)
表示切り替えをしなくても、同じことが起こります。
例えば、calrunの中身を、非常に手間のかかる計算を呼び出すルーチンにした場合、
その計算が終わるまではウインドウが表示されません。

暁さんのおっしゃるとおり、完全に表示が完了する前に、計算が始まってしまって、
それが終わるまで表示の作業が行われないように見受けられます。
つまり、mainの
TestClass tc =new TestClass();
tc.calrun();
で、コンストラクタが完了する前に、calrunが始まってしまい、そちらの作業のみしか進まなくなっているように思えるのですが。

何か勘違いしているでしょうか?

よろしくお願いします。


山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2007-04-22 00:02
Swing はシングルスレッドで動いているので、イベントが発生した最中に長い処理をやっているとその処理が終わるまで画面描画の処理は行われません。
画面描画を妨げず、長時間かかる処理を行う場合は別のスレッドを起こしてそちらで行いましょう。
you5
会議室デビュー日: 2007/04/21
投稿数: 4
投稿日時: 2007-04-23 12:34
インギ様
ご回答ありがとうございました。
おかげさまで、1週間悩みましたが、解決しました。
大変勉強になりました。
これは基本ですか?
ただ、呼び出し元で長い処理を行っている訳ではないと思っていますが、アプリケーションウインドウが起動していること自体、何か作業が行われていると考えるべきなのでしょうか?
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2007-04-23 12:45
Swing のフレームを作るだけで専用のスレッドが一つ起動して、画面描画やイベントハンドリングに関する一切の処理を受け持つことになります。
また、自分で作ったスレッド内から画面描画に関する処理をしたい場合は直接 UI オブジェクトを操作せず SwingUtilities#invokeLater() でその専用スレッドにお願いするのが作法です。

Swing を体系的に学ぶと「かならず知っておかなければいけない基礎知識」として勉強することになるみたいですが、私も結構最近まで知りませんでした
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-04-23 16:38
詳しい話をし始めると大変なのですが、swingでは描画やイベントの
管理のために管理用のThreadが暗黙に起動します。
適切なやりかたをしないとこれらのThreadが終了しないがために
プログラムがずっと残ってしまうのですね。
デーモンスレッドではないThreadが全て終了しないと終わらないですから。

swingでフレームを表示する際には
コード:
public void main(String[] args) {
  // フレームを作る
  final JFrame frame = new JFrame();

  // invokeLaterでフレームを表示
  SwingUtilities.invokeLater(new Runnable(){
    public void run() {
      frame.setVisible(true);
    }
  });


としてやるのが基本です。

詳しくはイベントディスパッチスレッドについて勉強する必要があります。
swingコンポーネントの状態を変更する際はイベントディスパッチスレッドで
行う必要があります。つまり、invokeLater()を使う必要がある。
ややこしいところは、ActionListenerのactionPerformed()の中に
処理を書いた場合などはactionPerformed()自体がイベントディスパッチスレッド
から呼び出されるものなのでinvokeLater()するとうまく動かなかったりする…。

Eclipseを使っているようなので、各所でブレークポイントを張ってみてください。
スタックトレースを見ると、イベントディスパッチスレッドから呼ばれているのが
わかると思います。
また、自分が起動したわけでもないのにいろいろなスレッドが動いていることも
わかるのではないでしょうか。
you5
会議室デビュー日: 2007/04/21
投稿数: 4
投稿日時: 2007-04-23 18:05
皆様 ご丁寧な回答ありがとうございました。

初心者のため、読んだだけでは理解できないことも多々ありますが、これからいろいろ勉強しながら、トライしてみます。
その上で再度ご質問させて頂くこともあるかと思いますが、よろしくお願いします。

1

スキルアップ/キャリアアップ(JOB@IT)