[Java] 90 JavaFX Paneの詳細位置設定

TabヘッダやTabコンテンツの位置設定等に苦労していましたが、ようやく慣れてきました。この手の設定はSwingの方が断然楽です。

コードから設定する場合はPaneのsetStyleメソッド、CSSから設定する場合は各Paneのpaddingを調整します。ネット情報でコードからはCSSの内容をいじれないと書いていることがありますが、そんなことはありません。

特にTabPaneのタブの位置を調整するのに手こずりました。GitHubで配布しているcaspitan.cssやmodena.cssといった代表的なスタイルシートを元に自分なりにアレンジしました。

TabPaneの場合は以下の様に設定してタブをほぼ中央にもってきました。こういうのは私の様なCSS初心者が一から書いているとなかなか思いつかないです。”:top”を付けられずに立ち往生したでしょう。ネット情報では最後の砦のStackOverflow英語版でも類似の質問に対してまともな回答がありませんでした。今回はcaspitan.cssのおかげでなんとかできました。

CSSを使いこなすには書式を知っているか否かが全てでロジカルにアプローチできないのが厄介です。JavaFXが標準ライブラリから外されてしまった理由が何となく分かる様な気がします。

.tab-pane:top > .tab-header-area {
    -fx-background-insets: 0, 0 0 1 0;
    -fx-padding: 2 80 0 80 ;
}

[Java] 89 JavaFX TabPaneの設定

SwingアプリのJavaFX版作成に取り組んでいます。FXMLファイルは使いません。

CSSで細かい設定ができるので、Swingの見た目の野暮ったさは解消できそうです。

複数のタブを作成する際にそれぞれのタブのインスタンスを配列にしてみました。またタブ名のところに色付きの長方形を入れています。

参考までにコードを記しておきます。

<一部を抜粋>
TabPane leftpane = new TabPane();

Tab[] tabs = {new Tab0(),new Tab1(),new Tab2(),new Tab3(),new Tab4(),new Tab5()};
String[] title = {"Tab0","Tab1","Tab2","Tab3","Tab4","Tab5"};
Color[] colors = {null,Color.RED,Color.YELLOW,Color.BLUE,Color.BLACK};

for (int i = 0; i < 6; i++) {
	Tab tab = tabs[i];
	if (i >=1 && i<= 4){
		Rectangle r = new Rectangle();
			r.setWidth(20);
			r.setHeight(20);
			r.setArcWidth(10);
			r.setArcHeight(10);
			r.setFill(colors[i]);
			r.setOpacity(0.7);
		tab.setGraphic(r);
		tab.setText(title[i]);
	}else{
		tab.setText(title[i]);
	}
	leftpane.getTabs().add(tab);
}

以下、各タブのクラスを記述

[Java] 88 Apple公証方法の概要

[macOS Big Sur 11.6 , Mac mini M1 , Mac mini Intel]

最近開発したアプリをApp Storeに登録しようとしましたが、個人開発の場合は実名をApp Storeで公開することになるので、公証だけしてもらい自分のサイトにアップしました(署名するため結局公開になります)。

Appleの公証をパスできるようになるまでおよそ2日半掛かりました。成功するまでに”Your Mac software was not notarized.”のメールを38通受信しています。過去最高と言っていいくらいの壁でしたが、粘り強く食い下がって何とか解決できました。

Xcodeで開発した場合はIDE内で簡単に公証を受けられる様ですが、今回はJavaなのでほとんどターミナルでのコマンド処理でした。

流れは以下の通りです。あくまで私のケースでの進め方ですから参考程度にしていただければと思います。

1. Developer ID Applicationの証明書を発行する。
2. jar実行ファイルに署名する。
3. jpackageコマンドでappファイルを作成(引数は–type app-image他)して署名する。
4. appファイルをzipファイルに圧縮する。
5. zipファイルをxcrun altoolコマンドでAppleに提出する。
6. 1回で公証はまず通らない。ログを読んで署名ができていない(“The signature of the binary is invalid.”)と指摘されているファイルにappファイルからアクセスして署名する。
7. 追加署名したらzipファイルに圧縮して再提出。これを何回か繰り返す。
8. 署名漏れを完全にクリアすると公証が通った。
9. appファイルにステープラーでチケットを紐づける。
10. zipファイルに圧縮して自サイトにアップロードする。

dmgファイルで提出して署名漏れを指摘されても該当ファイルにアクセスできないのでappファイルにするのがポイントです。appファイルであれば中のContentsにアクセスして追加署名が可能です。

2回目以降は署名が必要なファイルが分かっているので提出前に全て署名しておけば一発で通ります。entitlements.plistの内容によるのかもしれませんが、私の場合はlibjli.dylibにも署名が必要でした。

なお公証を受けたファイルから署名の内容を確認できるため、結局実名は公開することになります。

[Java] 87 Swingコンポーネントの形状変更 Look&Feel

結構使いそうなのでメモ書きしておきます。

import javax.swing.UIManager;

void setLAF() {
		try {
			System.out.println(UIManager.getSystemLookAndFeelClassName());
			// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
			// javax.swing.plaf.metal.MetalLookAndFeel
			// com.sun.java.swing.plaf.gtk.GTKLookAndFeel
			// com.sun.java.swing.plaf.motif.MotifLookAndFeel
			// com.sun.java.swing.plaf.windows.WindowsLookAndFeel
		} catch (Exception e) {
			e.printStackTrace();
		}
}

[Java] 86 jar実行ファイルの作成

# ソースコードのディレクトリに移動する
cd /projects/FileRemover/src

# ソースコードをJavaバイトコードにコンパイルする
javac App.java

# jar実行ファイルを作成する
jar cvfm FileRemover.jar manifest.mf *.class

# manifest.mfの内容(必ず改行する)
Main-Class: App

# jarを実行(ダブルクリックも可)
java -jar FileRemover.jar

[Java] 85 Swing 23 JTextPaneのHTML設定

[macOS catalina 10.15.7 , IDE : Eclipse 2021-06 , JRE : Java 16]

検討の流れでJEditorPaneをJTextPaneに変更しました。編集はしないので本来はこの選択が正しいです。

ある程度CSSでHTMLの設定は可能ですが、tableの列ごとにテキストの位置を変えることはできませんでした。全体のテキスト位置は設定可能です。設定にはCSSファイルあるいはHTMLEditorKitを使います。

結局、tableの各列テキスト位置はHTML内で設定しました。

話変わって、LTS版のJava 17が来月9月14日にリリースされるようです。標準ライブラリ入りをキープしているSwingは引き続き使えると思いますが、早く動作確認したいところです。

調べるほどにJavaFXの近年の失速ぶりが明確になりました。のめり込まないうちにSwingに移行して結果的には正解でした。6月頃に「これからはJavaFXの時代だ」と早合点して中古本を1冊購入したのはご愛嬌です。

2014から18年頃のWeb記事で、JavaFXが標準ライブラリ定着となりSwingが廃止決定、などといったガセ情報が結構目立っています。しかし2018年9月リリースのJava11以降は標準ライブラリ除外となっています(OpenJFKとして開発は継続)。JavaFXの進歩性には私も心躍りましたが、残念至極です。

ところで秋に発表予定のM1X MacBook。外部ディスプレイを2台使えるようになるのでしょうか(購入にあたってゆずれない条件)。

グラフィック性能が何割増しではなく数倍になるという噂を聞いて嫌な予感しかしません。拡張性を犠牲にして性能向上を優先?ともあれAppleシリコンMacがJavaFXのような一発屋にならないことを祈るばかりです。

import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;

HTMLEditorKit editorKit = new HTMLEditorKit();
StyleSheet css = editorKit.getStyleSheet();
css.addRule("table {background-color: #faf0e6;}");
StringBuilder HTMLcode = new StringBuilder();

int count = 0;
HTMLcode.append("<html><head></head>" + "<table" + " " + "border='1'" + " " + "id='dataframe'>" + "<thead><tr>");
for (ArrayList<String> raceList: raceListConSorts){
    if (count == 0){
        for (int i = 0 ; i < raceList.size() ; i++){
            HTMLcode.append("<th>" + raceList.get(i) + "</th>");}
        HTMLcode.append("</tr></thead><tbody>");
        HTMLcode.append("<tr>");
        count++;
    }else{
        for (int i = 0 ; i < raceList.size() ; i++){
           	if (i == 4) {
                String race_html = String.format("/%s_%s_%03d.html",now,name_latin,count);
                HTMLcode.append("<td>");
                HTMLcode.append("<a href='" + race_html + "'>" + raceList.get(i) + "</a>");
                HTMLcode.append("</td>");
           	}else if (((i>=5)&&(i<=9))||(i==19)){
                HTMLcode.append("<td align='right'>" + raceList.get(i) + "</td>");
            }else {
                HTMLcode.append("<td>" + raceList.get(i) + "</td>");
            }
        }
        HTMLcode.append("</tr>");
        count++;
    }
}
HTMLcode.append("</tbody></table></html>");

[Java] 84 Swing 22 JEditorPaneハイパーリンク・クリック後のHTMLファイル作成 複数table各列の設定

前回の続きです。HTMLのレイアウトを一応完成させました。

列ごとの設定には少し手間取りました。複数tableの設定に関するネット情報は調べた範囲では見当たりませんでした。まあ情報が乏しくても推測で分かると思います。

body {
    background-color: #f0f8ff;
}
th {
    text-align: center;
    }
.container {
    display: block;
}
#tableA{
    background-color: #ffffe0;
    margin: 15px 5px 15px 5px;
}
#tableB{
    background-color: #e0ffff;
    margin: 15px 5px 15px 5px;
}
#tableC{
    background-color: #7fffd4;
    margin: 15px 5px 15px 5px;
}
#tableB tr td:nth-of-type(1){text-align: right} /* 着順 */
#tableB tr td:nth-of-type(2){text-align: right} /* 枠番 */
#tableB tr td:nth-of-type(3){text-align: right} /* 馬番 */
#tableB tr td:nth-of-type(4){width: 160px;padding:0 0 0 5px} /* 馬名 */
#tableB tr td:nth-of-type(5){text-align: center} /* 性齢 */
#tableB tr td:nth-of-type(6){text-align: right;width: 45px} /* 斤量 */
#tableB tr td:nth-of-type(7){width: 80px;padding:0 0 0 5px} /* 騎手 */
#tableB tr td:nth-of-type(8){width: 60px} /* タイム */
#tableB tr td:nth-of-type(9){text-align: left} /* 着差 */
#tableB tr td:nth-of-type(10){text-align: left} /* 通過 */
#tableB tr td:nth-of-type(11){text-align: right;width: 45px;} /* 上り */
#tableB tr td:nth-of-type(12){text-align: right; width: 45px} /* 単勝 */
#tableB tr td:nth-of-type(13){text-align: right} /* 人気 */
#tableB tr td:nth-of-type(14){width: 85px;padding:0 0 0 5px} /* 馬体重 */
#tableB tr td:nth-of-type(15){width: 100px} /* 調教師 */
#tableB tr td:nth-of-type(16){padding:0 0 0 5px} /* 馬主 */
#tableB tr td:nth-of-type(17){text-align: right} /* 賞金 */

[Java] 83 Swing 21 JEditorPaneハイパーリンク・クリック後のHTMLファイル作成 各tableの設定

HTMLの各tableにidを付けて色設定等を変えてみました。

同じ文字列(class=”dataframe”)をそれぞれ違う文字列(id=”tableA”他)に変えるには、このような野暮ったい方法しか思いつきませんでした。

pandasではtableの線が2重線なので1重線にして色を付けました。

<CSVファイルの読み込み以降>

try:
    df2 = pd.read_csv(racefile_path,encoding="shift_JIS")
except UnicodeDecodeError:
    df2 = pd.read_csv(racefile_path,encoding="UTF-8")

# df2を3つのtableに分割
dfA = df2[['1 日付','2 開催','3 レース','4 レース名','5 条件','6 コース','7 天候','8 馬場状態','9 発走時刻']]
dfB = df2[['着順','枠番','馬番','馬名','性齢','斤量','騎手','タイム','着差','通過','上り','単勝','人気','馬体重','調教師','馬主','賞金(万円)']]

dfB2 = dfB[:-3]
dfC = dfB[-2:]
print(dfC)

dfC2 = dfC[['着順','枠番']]
print(dfC2)

# dfAのデータ行のみ抽出
row_num = len(df2)-3
print(row_num)

# データ行以外の行番号をリスト化
row_list = [row for row in range(0,row_num + 3) if not row==row_num]
print(row_list)

dfA2 = dfA.drop(dfA.index[row_list])
dfA3 = dfA2.rename(columns={'1 日付': '日付','2 開催': '開催','3 レース': 'レース','4 レース名': 'レース名','5 条件': '条件','6 コース': 'コース','7 天候': '天候','8 馬場状態': '馬場状態','9 発走時刻': '発走時刻'})

dfB3 = dfB2.fillna('')
print(dfB3.dtypes)

try:
    dfB3['着順'] = pd.to_numeric(dfB3['着順'], downcast='signed')
except Exception as e:
    print(e)

dfB3['枠番'] = pd.to_numeric(dfB3['枠番'], downcast='signed')
dfB3['馬番'] = pd.to_numeric(dfB3['馬番'], downcast='signed')
dfB3['人気'] = pd.to_numeric(dfB3['人気'], downcast='signed')
print(dfB3.dtypes)

html_string = '''
<html>
    <head>
        <meta charset="UTF-8">
        <title>{raceID}</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            {tableA}
            {tableB}
            {tableC}
        </div>
    </body>
</html>
'''

with open(html_file,'w') as f:
    f.write(html_string.format(tableA=dfA3.to_html(index=False),tableB=dfB3.to_html(index=False),tableC=dfC2.to_html(index=False,header=False),raceID=raceID))

with open(html_file,'r') as f:
    html = f.read()

htmlH = html.split('<table border="1" class="dataframe">')[0]
htmlA = html.split('<table border="1" class="dataframe">')[1]
htmlB = html.split('<table border="1" class="dataframe">')[2]
htmlC = html.split('<table border="1" class="dataframe">')[3]

htmlA2 = '<table id="tableA" border="1" style="border-collapse: collapse; border-color: #add8e6">' + htmlA
htmlB2 = '<table id="tableB" border="1" style="border-collapse: collapse; border-color: #ffb6c1">' + htmlB
htmlC2 = '<table id="tableC" border="1" style="border-collapse: collapse; border-color: #f0e68c">' + htmlC

html_new = htmlH + htmlA2 + htmlB2 + htmlC2

with open(html_file,'w') as f:
    f.write(html_new)
body {
    background-color: #f0f8ff;
}
th {
    text-align: center;
    }
.container {
    display: block;
}
#tableA{
    background-color: #ffffe0;
    margin: 15px 5px 15px 5px;
}
#tableB{
    background-color: #e0ffff;
    margin: 15px 5px 15px 5px;
}
#tableC{
    background-color: #7fffd4;
    margin: 15px 5px 15px 5px;
}

[Java] 82 Swing 20 JEditorPaneハイパーリンク・クリック後のHTMLファイル作成 table列のデータ型変更

もう一息のところまで出来ました。

データ型の変更でastype関数が効かないため、to_numeric関数を使いました。to_numeric関数の取り扱いはドキュメントを読まないと分かりませんでした。中級者向けでしょうか。

あとは2番目のtableのいくつかの列を右寄せにして一応完成になります。

各tableにIDを付けて2番目だけ書式設定する必要がありますが、少し工数が掛かりそうなので次回以降の記事で紹介します。

このアプリを作り上げてきて、ようやく実用に耐えるレベルに達したように思います。インターネット接続が従量制だった頃であれば、どれだけコストが浮いたことでしょう。

<CSVファイルの読み込み以降>

try:
    df2 = pd.read_csv(racefile_path,encoding="shift_JIS")
except UnicodeDecodeError:
    df2 = pd.read_csv(racefile_path,encoding="UTF-8")

# df2を複数のTableに分割
dfA = df2[['1 日付','2 開催','3 レース','4 レース名','5 条件','6 コース','7 天候','8 馬場状態','9 発走時刻']]
dfB = df2[['着順','枠番','馬番','馬名','性齢','斤量','騎手','タイム','着差','通過','上り','単勝','人気','馬体重','調教師','馬主','賞金(万円)']]

dfB2 = dfB[:-3]
dfC = dfB[-2:]
print(dfC)

dfC2 = dfC[['着順','枠番']]
print(dfC2)

# dfAのデータ行のみ抽出
row_num = len(df2)-3
print(row_num)

# データ行以外の行番号をリスト化
row_list = [row for row in range(0,row_num + 3) if not row==row_num]
print(row_list)

dfA2 = dfA.drop(dfA.index[row_list])
dfA3 = dfA2.rename(columns={'1 日付': '日付','2 開催': '開催','3 レース': 'レース','4 レース名': 'レース名','5 条件': '条件','6 コース': 'コース','7 天候': '天候','8 馬場状態': '馬場状態','9 発走時刻': '発走時刻'})

dfB3 = dfB2.fillna('')
print(dfB3.dtypes)

# 中(止)、除(外)、取(消)への対応
try:
    dfB3['着順'] = pd.to_numeric(dfB3['着順'], downcast='signed')
except Exception as e:
    print(e)

dfB3['枠番'] = pd.to_numeric(dfB3['枠番'], downcast='signed')
dfB3['馬番'] = pd.to_numeric(dfB3['馬番'], downcast='signed')
dfB3['人気'] = pd.to_numeric(dfB3['人気'], downcast='signed')
print(dfB3.dtypes)

html_string = '''
<html>
    <head>
        <meta charset="UTF-8">
        <title>{raceID}</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            {tableA}
            {tableB}
            {tableC}
        </div>
    </body>
</html>.
'''

with open(html_file,'w') as f:
    f.write(html_string.format(tableA=dfA3.to_html(index=False),tableB=dfB3.to_html(index=False),tableC=dfC2.to_html(index=False,header=False),raceID=raceID))

[Java] 81 Swing 19 JEditorPaneハイパーリンク・クリック後のHTMLファイル作成 table分割

CSVファイル由来のTableを3つに分割し、縦に並べたHTMLファイルを作成しました。

大分見栄えが良くなりました。あともう少しです。

CSVデータを簡単にHTMLにしてくれるpandasは本当に頼りになるツールです。他のスクリプト言語でpandasに相当するものは存在するのでしょうか。

<CSVファイルの読み込み以降>

try:
    df2 = pd.read_csv(racefile_path,encoding="shift_JIS")
except UnicodeDecodeError:
    df2 = pd.read_csv(racefile_path,encoding="UTF-8")

# df2を複数のTableに分割
dfA = df2[['1 日付','2 開催','3 レース','4 レース名','5 条件','6 コース','7 天候','8 馬場状態','9 発走時刻']]
dfB_pre = df2[['着順','枠番','馬番','馬名','性齢','斤量','騎手','タイム','着差','通過','上り','単勝','人気','馬体重','調教師','馬主','賞金(万円)']]

dfB = dfB_pre[:-3]
dfC_pre = dfB_pre[-2:]
print(dfC_pre)

dfC = dfC_pre[['着順','枠番']]
print(dfC)

# dfAのデータ行のみ抽出
row_num = len(df2)-3
print(row_num)

# データ行以外の行番号をリスト化
row_list = [row for row in range(0,row_num + 3) if not row==row_num]
print(row_list)

dfA2 = dfA.drop(dfA.index[row_list])

html_string = '''
<html>
    <head>
        <meta charset="UTF-8">
        <title>{raceID}</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            {tableA}
            {tableB}
            {tableC}
        </div>
    </body>
</html>.
'''

with open(html_file,'w') as f:
    f.write(html_string.format(tableA=dfA2.to_html(index=False),tableB=dfB.to_html(index=False),tableC=dfC.to_html(index=False,header=False),raceID=raceID))