[Java] 50 JavaFX 08 文字コード変換ツール

GUI作成についてはPythonのtkinterからJavaFXへの移行を目指しています。

やはりScene Builderを使ってGUIをデザインするのが楽ですから。

今は文字コード変換ツールを作成しています。

文字コード判定のところまで書きました。文字コード判定クラスFileCharDetectorはネットから拝借して私なりに手を入れました。

早速TextAreaの高さをTextFieldに合わせられない不具合が発生していますが後日サイズ調整します。

package javafx_conv;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
import javafx.event.ActionEvent;

public class GuiController {

    @FXML
    private Button button1;
    @FXML
    private Button button2;
    @FXML
    private TextField text1;
    @FXML
    private TextArea textarea1;

    String path;
    String code;

    @FXML
    void onButton1Action(ActionEvent event) {
        System.out.println("変換ボタンを押しました!");
        // 作成中
    }

    @FXML
    void onButton2Action(ActionEvent event) {
        path = text1.getText();
        FileCharDetector fd = new FileCharDetector(path);

        try{
            code = fd.detector();
        }
        catch (Exception e) {
            System.out.println(e);
        }

        if (code != null){
            textarea1.setText(code);
        }
        else{
            textarea1.setText("判定不可");
        }

    }
}

参考サイト

[Java] 49 JavaFX 07 JDK11.0.11で文字化け

[macOS 10.15.7 Catalina]

Javaがらみではちょっとしたことでトラブルに巻き込まれるので、都度記録しておきます。

今回はJDKのバージョンによるJavaFXの文字化けです。

VScodeのsettings.jsonでJDKのバージョンを以下のように設定していました。

"java.home": "/Library/Java/JavaVirtualMachines/jdk-11.0.8.jdk/Contents/Home"

jdk-11.0.8.jdkからjdk-11.0.11.jdkに変更したところ文字化けが発生しました。ちなみにjdk13ではこのようなトラブルはありません。

私の環境だけかもしれませんが、今後も似たようなトラブルになった時のためにメモしておきます。前にもあったmacOS 10.15 Catalinaの問題のような気もします。

Javaは開発環境構築とトラブル対応のコストがかかり過ぎるがつくづく残念です。この傾向はEclipseのようなIDEでも、VScodeのようなエディタでも変わりません。

以前も書きましたが、VScodeを使った軽量環境構築はJava用プロジェクト管理ツールのMavenに出会うまでは本当に泥沼でした。

[Java] 48 Build failed, do you want to continue?

VScodeでJavaコードをビルドしようとするとタイトルのエラーが出ました。

Proceedボタンで先に進むことはできますが、どうやってもこのエラーを消せなくて困りました。

ネット検索すると、workspaceStorageフォルダを空にすればよいとか解決策が紹介されていたので試してみるもうまくいかず。

コマンドパレットから”Java: Open Java Language Server log file”でログを確認すると以下の詳細メッセージがありました。

ENTRY org.eclipse.jdt.ls.core 4 0 2021-07-30 08:47:22.370
!MESSAGE Error occured while building workspace. Details: 
 message: Syntax error on tokens, delete these tokens; code: 1610612969; resource: /Java/projects/javafx_conv/src/test/java/test/._AppTest.java; line: 1
 message: Syntax error on tokens, delete these tokens; code: 1610612969; resource: /Java/projects/javafx_conv/src/test/java/test/._Main.java; line: 1
 message: Syntax error on tokens, delete these tokens; code: 1610612969; resource: /Java/projects/javafx_conv/src/test/java/test/._SampleController.java; line: 1

原因となっている3つのファイルを削除するとあっさり直りました。

同じエラーメッセージでも原因は様々なので、むやみにググらずログを確認して対処するべきでした。今回はざっくりとしすぎたメッセージゆえ尚更でしたね。

[Java] 47 Swing 03 getActionCommand

ボタンを押した時のアクションについてコードを書き、完成させました。

とりあえずバージョン0.1が出来上がったので、これから少しずつ機能を増やしていきます。

ガワ(GUI部分)はEclipseのサポートで何とかなりそうですし、アプリ機能を正確かつスムーズにコーディングするスキルを磨いていきたいです。

<途中から>

@Override
	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();
		
		if (cmd.equals("Button1")){
			String name = text1.getText();
			
			System.out.println("競走馬名取得 " + name);
			
			Horse.IDSearch(name);
			ArrayList<String> nameAndIDCon = Horse.IDSearch(name);
	    	
			Integer count = nameAndIDCon.size()-1;
	      	    	
	    	StringBuilder result = new StringBuilder();
	    	for (String ele:nameAndIDCon) {
	    		result.append(ele);
    		}
	    	
	    	String resultStr = result.toString();
			area.append(resultStr + "\n");
			
			if (count >= 2) {
				area.append("\n" + "該当する馬が複数います。番号を入力してください。");
				nameAndIDCon.subList(0,1).clear();
				area.append("\n" + nameAndIDCon + "\n");
				
	            int i = 1;
	            for (String s : nameAndIDCon) {
	            	area.append(i + "  " + s + "\n");
	                i = i + 1;
                }
            }
	        else if (count == 0){
	        	area.append("該当する馬はいません"+ "\n");
        	}
	        else {
	            String name2 = nameAndIDCon.get(0);
	            String ID = nameAndIDCon.get(1);

	            area.append(name2 + "のIDは" + ID + "です"+ "\n");
	    
	    		ArrayList<ArrayList<String>> raceList = Horse.raceSearch(nameAndIDCon);
	            ArrayList<ArrayList<String>> raceListCon = Horse.raceSearch2(raceList);
	            Horse.toHTML(raceListCon,nameAndIDCon);
	            
              // HTMLファイルのパスを取得しJEditorPaneに埋め込む
	            path = HTMLPath.main();
	            System.out.println(path);
	            	            
	    		try {
	    			editorPane.setPage(new File(path).toURI().toURL());
	            }
	    		catch (IOException e1) {
	    			e1.printStackTrace();
	    		}
            }
		}
		else if (cmd.equals("Button2")){
			String text3 = text2.getText();
	    	System.out.println(text3);
	    	
	    	int numID = Integer.parseInt(text3);
	    	System.out.println(numID);
	    	
	    	String name3 = text1.getText();
	    	System.out.println(name3);
	    	
	    	ArrayList<String> nameAndIDCon = Horse.IDSearch(name3);
	    	String ID =  nameAndIDCon.get(numID);
	    	ArrayList<String> nameAndIDCon2 = new ArrayList<String>();
	    	nameAndIDCon2.add(name3);
	    	nameAndIDCon2.add(ID);
	    	
	    	ArrayList<ArrayList<String>> raceList = Horse.raceSearch(nameAndIDCon2);
	        ArrayList<ArrayList<String>> raceListCon = Horse.raceSearch2(raceList);
	        Horse.toHTML(raceListCon,nameAndIDCon2);
	        
	        path = HTMLPath.main();
	        System.out.println(path);
	        
    		try {
    			editorPane.setPage(new File(path).toURI().toURL());
            }
    		catch (IOException e1) {
    			e1.printStackTrace();
    		}
		}
	}
}
※海外レースのデータは含まれていません

[Java] 46 Swing 02 パネルとコンポーネントの配置

contentPaneにパネルとコンポーネントを配置しました。

コンポーネントを左寄せにする方法で少しだけ手間取ったので記録を残しておきます。

SwingはPythonのtkinterに使用感が似ています。ただデザインタブで配置が視覚的にできる分、こちらの方がやや優勢でしょうか。

使い勝手で申し分のないJavaFXが使えないのはつくづく残念です。

あとはボタンアクションを追記し、標準出力をPAGE_ENDに表示させるなどして一応完了となります。

package horse_search;

<importは略>

public class Main extends JFrame {

	private JPanel contentPane;
	private JTextField textField;

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Main frame = new Main();
					frame.setVisible(true);
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	public Main() {
		// contentPane設定
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 1500, 750);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);

   // CENTER設定
		JEditorPane editorPane = new JEditorPane();
		contentPane.add(editorPane, BorderLayout.CENTER);
		editorPane.setContentType("text/html");
		try {
			editorPane.setPage(new File("シャフリヤール.html").toURI().toURL());
        }
		catch (IOException e) {
			e.printStackTrace();
		}
		
		// PAGE_START設定
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout(FlowLayout.LEFT));
		contentPane.add(panel, BorderLayout.PAGE_START);
		
		JLabel label = new JLabel("競走馬名");
	    JTextField text = new JTextField(20);
	    JButton btnNewButton = new JButton("検索");
	    textField = new JTextField(3);
	    JButton btnNewButton_1 = new JButton("確定");
	    	    
	    panel.add(label);
	    panel.add(text);
	    panel.add(btnNewButton);
		panel.add(textField);
		panel.add(btnNewButton_1);
		
		// PAGE_END設定
		JPanel panel2 = new JPanel();
		panel2.setLayout(new FlowLayout(FlowLayout.LEFT));
		contentPane.add(panel2, BorderLayout.PAGE_END);
		
		JTextArea textArea = new JTextArea(15,30);
		textArea.setLayout(new FlowLayout(FlowLayout.LEFT));
		panel2.add(textArea);

[Java] 45 Swing 01 HTMLファイルの表示

JavaFXでWebページの表示はできたのですが、ローカルHTMLファイルの方がどうしてもできません。

JavaFXよりもさらに古いSwingというツールを使ってみたところ、あっけなく解決しました。

JavaFXで色々試しても打開できず、やはりmacOSではJavaよりもSwiftなのかと半ば諦めていました。

SwingはJava8以前でしか使えませんが、JavaFXが私の手に余るので仕方がないです。

あとは競争馬名を入力するテキストフィールドやボタンを配置して完成です。

2021/8/28追記:SwingはJava9以降でも使えます。勘違いしていました。

package swing;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;

import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Sample extends JFrame {

	private JPanel contentPane;

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Sample frame = new Sample();
					frame.setVisible(true);
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	public Sample() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		JEditorPane editorPane = new JEditorPane();
		contentPane.add(editorPane, BorderLayout.CENTER);
		editorPane.setContentType("text/html");
		try {
			editorPane.setPage(new File("シャフリヤール.html").toURI().toURL());
        }
		catch (IOException e) {
			e.printStackTrace();
		}
	}
}

[Java] 44 JavaFX 06 WebViewはJava11では動かず

前回記事でJavaFXにあるWebViewの導入に苦労していることを書きましたが、原因は開発環境にありました。

Catalina起因の文字化けトラブルに巻き込まれた経緯からmacOSに疑いの目を向けていました。かといってWindowsに宗旨替えするわけにもいかず、Javaの学習を中断しSwiftへの移行を真剣に検討していたのですが、どう見ても瑕疵のないネット公開のコードも動かないため、開発環境に問題があるのではと考えました。

検証の手間のかからない順に、Javaのバージョン、OS(Windows機は持っているので)、macOSのバージョンとなるところ、まず始めにJava11からJava8に変えてみたらあっさり解決しました。

macOSを取るか、Java(Windows)を取るかという選択を迫られずに済みましたが、かなり後ろ向きな解決方法なので、将来的には他のGUI作成ツールを検討することになるでしょう。

[Java] 43 JavaFX 05 2画面の表示

同じ画面に検索結果のHTMLを埋め込もうとしましたが、どうにもうまくいかないのでまずは別画面に反映させることにします。

空の別画面を作成してみました。ここから色々試してみます。

なおEclipseのエディタではカッコの色分けができないので閉じカッコは文末から通常の位置に戻しました。波カッコにもだいぶ慣れて違和感がなくなってきました。

package horse_search;
	
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class Main extends Application {
	@Override
	public void start(Stage stage) {
		try {
			stage.setScene(new Scene(new AnchorPane(), 400, 200));
			Parent root = FXMLLoader.load(getClass().getResource("App.fxml"));
			Scene scene = new Scene(root);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			stage.setScene(scene);
			stage.setTitle("Horse Search");
		
			Stage stage2 = new Stage();
			stage2.initOwner(stage);
			stage2.setScene(new Scene(new AnchorPane(), 800, 600));
			Parent root2 = FXMLLoader.load(getClass().getResource("App2.fxml"));
			Scene scene2 = new Scene(root2);
			scene2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			stage2.setScene(scene2);
			stage2.setTitle("検索結果");
			
			stage.show();
			stage2.show();
			
			stage2.setX(stage.getX() + 100);
		  stage2.setY(stage.getY() + 100);
		}
		
		catch(Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		launch(args);
	}
}

[Java] 42 JavaFX 04 Controllerファイルの作成

Javaで初めて作成するGUIアプリの進捗です。

Controllerファイルを作成し、競走馬名から成績を検索してHTMLファイルにまとめるという最低限の機能を持たせました。mySQLの操作については別ファイルに分割して書いています。

Scene Builderでコンポーネントをドラッグ&ドロップで並べられるのは本当に便利です。Pythonのtkinterではコンポーネントの指定にウィジェットinfoを使うのに対し、こちらはIDだけでいいのでかなり楽です。

テキストエリアに自動的にスクロールバーが付くのもいいですね。デザインソフトはみな同じなのかもしれませんが、Adobe XDに操作性が似ていてすぐ馴染みました。

次はHTMLファイルをテキストエリアに貼り付けたような感じにしたいので、これから調査します。

package horse_search;

import java.util.ArrayList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

public class SampleController {
    @FXML
    private TextArea area1;
    @FXML
    private Label label1;
    @FXML
    private Button button1;
    @FXML
    private Button button2;
    @FXML
    private TextField textField1;
    @FXML
    private TextField textField2;
    
    @FXML
    void onButton1Action(ActionEvent event) {
    	String name = textField1.getText();
      
      // Horseクラスの戻り値を変数nameAndIDConに設定
    	ArrayList<String> nameAndIDCon = Horse.IDSearch(name);
    	
      // 変数nameAndIDConは競走馬名とIDのリストになっているのでID数は要素数から1を引いて算出
      Integer count = nameAndIDCon.size()-1;
      
      // 競走馬名とIDを結合させたプレ文字列を作成	    	
    	StringBuilder result = new StringBuilder();
    	for (String ele:nameAndIDCon) {
    		result.append(ele);}
    	
    // area1の先頭に変換した文字列を挿入
    	String resultStr = result.toString();
      area1.insertText(0,resultStr + "\n");
		
      // IDが2つ以上の場合は1つを選ばせる
      if (count >= 2) {
          // 末尾に文字列を挿入するため位置place1を算出
          String text1 = area1.getText();
          Integer place1 = text1.length() - 1;
          area1.insertText(place1,"\n" + "該当する馬が複数います。番号を入力してください。");

          // リストから先頭の競走馬名を削除
       nameAndIDCon.subList(0,1).clear();

          // 末尾に文字列を挿入するため位置place2を算出
          String text2 = area1.getText();
          Integer place2 = text2.length() - 1;
          area1.insertText(place2, "\n" + nameAndIDCon + "\n");
			  
          // 選択肢の作成
          int i = 1;
          for (String s : nameAndIDCon) {
              String text3 = area1.getText();
              Integer place3 = text3.length() - 1;
              area1.insertText(place3,i + "  " + s + "\n");
              i = i + 1;}}
        
        else if (count == 0){
            String text4 = area1.getText();
            Integer place4 = text4.length() - 1;
            area1.insertText(place4,"該当する馬はいません"+ "\n");}
        
        else {
            String name2 = nameAndIDCon.get(0);
            String ID = nameAndIDCon.get(1);
            
            String text5 = area1.getText();
            Integer place5 = text5.length() - 1;
            area1.insertText(place5,name2 + "のIDは" + ID + "です"+ "\n");

            ArrayList<ArrayList<String>> raceList = Horse.raceSearch(nameAndIDCon);
            ArrayList<ArrayList<String>> raceListCon = Horse.raceSearch2(raceList);
            Horse.toHTML(raceListCon,nameAndIDCon);}}
		
    // 該当馬が複数の場合は右のテキストフィールドに番号を入力して確定ボタンを押す。
    @FXML
    void onButton2Action(ActionEvent event) {
    	String text6 = textField2.getText();
    	System.out.println(text6);
    	
      // 文字列を整数に変換
    	int numID = Integer.parseInt(text6);
    	System.out.println(numID);
    	
    	String name3 = textField1.getText();
    	System.out.println(name3);
    	
    	ArrayList<String> nameAndIDCon = Horse.IDSearch(name3);
    	String ID =  nameAndIDCon.get(numID);
    	ArrayList<String> nameAndIDCon2 = new ArrayList<String>();
    	nameAndIDCon2.add(name3);
    	nameAndIDCon2.add(ID);
    	
    	ArrayList<ArrayList<String>> raceList = Horse.raceSearch(nameAndIDCon2);
      ArrayList<ArrayList<String>> raceListCon = Horse.raceSearch2(raceList);
      Horse.toHTML(raceListCon,nameAndIDCon2);}}

[Java] 41 JavaFX 03 Scene Builderの導入 Eclipse編

VScodeでのGUIアプリ製作は一旦見合わせ、Eclipseで進めることにしました。色々試行錯誤しているうちに大分UIにも慣れました。

Scene BuilderでGUI画面を作り、SampleControllerSkeletonの内容をコントローラのjavaファイルに写し、GUIの動作を追記するといった流れを学びました。

GUIアプリとしてはTextFieldに競走馬名を入力し、それを先日作成した競馬DB検索コードに取り込んで成績CSVファイルを作成させました。

とりあえず自分用の手本としてアップしておきます。なんでもない内容ですがここまで結構時間が掛かっています。

時間的イニシャルコストは消費したものの、Pythonのtkinterでの苦労は何だったんだと感じる位、操作性が優れている印象です。

一つのプログラミング言語にこだわるよりも適材適所で使い分けるのがいいみたいです。

import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		try {
			AnchorPane root = (AnchorPane)FXMLLoader.load(getClass().getResource("Sample.fxml"));
			Scene scene = new Scene(root);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			primaryStage.setScene(scene);
			primaryStage.show();}
		catch(Exception e) {
			e.printStackTrace();}}
	
	public static void main(String[] args) {
		launch(args);}}
package horse_search;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

public class SampleController {
  @FXML
    private TextArea area1;

  @FXML
    private Label label1;

  @FXML
    private Button button1;

  @FXML
    private TextField textField1;

  @FXML
    protected void onButton1Action(ActionEvent event) {
        String name = textField1.getText();
        System.out.println(name);
        Horse.main(null,name);}} 	
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.paint.*?>
<?import javafx.scene.effect.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="horse_search.SampleController">
   <children>
      <Label fx:id="label1" layoutX="14.0" layoutY="22.0" text="競走馬名" />
      <TextField fx:id="textField1" layoutX="71.0" layoutY="17.0" prefHeight="27.0" prefWidth="153.0" />
      <Button fx:id="button1" layoutX="231.0" layoutY="17.0" mnemonicParsing="false" onAction="#onButton1Action" prefHeight="27.0" prefWidth="52.0" text="検索" />
      <TextArea fx:id="area1" layoutX="20.0" layoutY="56.0" prefHeight="130.0" prefWidth="260.0" />
   </children>
</AnchorPane>
Scene BuilderのUI