[JavaScript] 03 ElectronによるGUIアプリ作成 その1

ElectronによるGUIアプリ作成に取り掛かりましたが、やはりJavaFXで要素を自在にドラッグする快適さが忘れられず、とりあえず下記コードまでにしてJRuby+JavaFXでの開発環境構築に移行します。

style.cssファイルにてグリッド設定を書いていて、あまりの面倒さに頭が拒否反応を示しました。paddingを書く気力が早々になくなりました。おそらくHTMLやCSSの文法が私には合わないのでしょう。Sassのようにfor文を使えたらまだいいんですが。

これなら多少不満があってもPythonのtkinterで書きます。せっかくJavaScriptとお近づきになれたのに残念です。

どうやら日々のプログラミング生活でロジカル思考が染み付いてしまい、ルールに縛られた上での思考が苦手になっているのでしょう。なので異なるプログラミング言語間の行き来は比較的容易でも、論理よりもルールが優先のスタイルシート言語等ではストレスが溜まるようです。

次にJavaScriptのコードを書くのはいつになることでしょう。

const {app, BrowserWindow} = require('electron');

// メインウィンドウ
let mainWindow;

function createWindow() {
  // メインウィンドウ作成
  mainWindow = new BrowserWindow({
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: false
    },
    width: 300, height: 150,
  });

  // メインウィンドウに表示するhtmlファイル指定
  mainWindow.loadFile('index.html');

  // メインウィンドウが閉じられたときの処理
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
    app.quit();
});

app.on('activate', () => {
  if (mainWindow === null) {
    createWindow();
  }
});
<html>
<head>
 <meta charset="UTF-8">
 <title>CHARACTER CODE CONVERTOR</title>
 <link rel="stylesheet" href="style.css">
</head>

<body>
    <div id="container">

    <text id="lbl1">Path</text>
    <input id="txt1" type="TEXT"></input>
    <button id="btn1" >判定</button>

    <text id="lbl2">コード</text>
    <input id="txt2" type="TEXT"></input>
    <button id="btn2" >変換</button>

    </div>
</body>
</html>
body {
    background-color: #91d8e8;
}
#container {
    display: grid;
    grid-template-rows: 50px 50px;
    grid-template-columns: 50px 50px 50px 50px;
    position:relative;
    top: 10px;
}
#lbl1 {
    grid-row:1;
    grid-column:1;
}
#txt1 {
    grid-row:1;
    grid-column: 2 / 4;
}
#btn1 {
    grid-row:1;
    grid-column:4;
}

#lbl2 {
    grid-row:2;
    grid-column:1;
}

#txt2 {
    grid-row:2;
    grid-column:2;
}

#btn2 {
    grid-row:2;
    grid-column:4;
}

[JavaScript] 02 JavaFX イベントハンドラの作成

FXMLファイルにおいて、イベントハンドラによるボタン押下時の動作内容等の設定はJavaScriptやJythonなどJSR 223にあるスクリプト言語でできるようです。

とりあえずJavaScriptで簡単なイベントを書いてみました。さてJythonの場合はどうすればいいのでしょう。

<?xml version="1.0" encoding="UTF-8"?>

<?language javascript?>
<?import javafx.scene.effect.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="125.0" prefWidth="300.0" style="-fx-background-color: khaki;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
	<fx:script>
		function onButton1Action(){
			text1.setText("JavaScriptテスト");
		}
	</fx:script>
	<children>
     	<Label layoutX="14.0" layoutY="15.0" prefHeight="44.0" prefWidth="69.0" text="Path" />
     	<Label layoutX="14.0" layoutY="64.0" prefHeight="44.0" prefWidth="69.0" text="文字コード" />

     	<TextField fx:id="text1" layoutX="49.0" layoutY="24.0" prefHeight="27.0" prefWidth="175.0" />
     	<TextField fx:id="text2" layoutX="90.0" layoutY="73.0" prefHeight="27.0" prefWidth="135.0" />

		<Button fx:id="button1" layoutX="240.0" layoutY="24.0" mnemonicParsing="false" onAction="onButton1Action(event);" text="判定" />
     	<Button fx:id="button2" layoutX="237.0" layoutY="69.0" mnemonicParsing="false" prefHeight="35.0" prefWidth="51.0" text="変換" />
	</children>
</AnchorPane>

[Python] 296 Jython 03 FXMLファイルの読み込み

軽い気持ちで取りかかったら見事にハマりました。

なかなか解決しなかったのは、launchメソッドの2番目の引数とFXMLファイルのパスの書き方です。

いわゆるヌルポ(NullPointerException)から脱するため、Thread.currentThread().getStackTrace()を走らせたり色々調べても解決の糸口を見出せず。

結局、引数はsys.argv、パスはプロジェクトディレクトリ直下の絶対パスでした。JavaではMainファイルからの相対パスになります(スラッシュなしのファイル名のみ)。スラッシュの有り無しは全く意識してませんでした。

EclipseからPyCharmに検討環境を移す寸前でStackOverFlowの英語版にヒントを見つけました。またしても海外のギークに助けられました。

GUI内のカタカナなど2バイト文字が明らかに中華系フォントなので、別途フォント指定が必要ですね。

from javafx.application import Application
from javafx.fxml import FXMLLoader
from javafx.scene import Scene
import sys

class JythonJavafx(Application):
    def start(self, stage):
        root = FXMLLoader.load(self.getClass().getResource('/test.fxml'))
        stage.setScene(Scene(root, 300, 125))
        stage.setTitle("CHARACTER CODE CONVERTOR")
        stage.show()

if __name__ == "__main__":
    Application.launch(JythonJavafx().__class__, sys.argv)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="125.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
    <center>
        <Label text="FXML ロード成功" BorderPane.alignment="CENTER" />
    </center>
</BorderPane>

[Python] 295 Jython 02 JavaFXプロジェクトの移植

JavaFXプロジェクトのJythonへの移植を試みています。

ガワは再現できたので次はFXMLの導入です。

Application.launchの引数でエラーになっていましたが、オブジェクトのクラス__class__で解決しました。

おそらくexe化はできないため、私にとってこのスキルの必要性は高くないです。気が向いた時にのんびり進めていきます。

from javafx.application import Application
from javafx.scene import Scene
from javafx.scene.layout import AnchorPane;

class Main(Application):
    def start(self, stage):
        root = AnchorPane()
        stage.setScene(Scene(root, 300, 125))
        stage.setTitle("CHARACTER CODE CONVERTOR")
        stage.show()

if __name__ == "__main__":
    Application.launch(Main().__class__, [])

[Python] 294 Jython 01 Eclipseでテスト

[macOS Catalina 10.15.7]

過去の遺物になりつつある感が否めないJython(ジャイソン)をたわむれにいじっています。

Hello JythonのPaneを表示させるのに丸1日かかりました。何かエラーになっているようですが、とりあえず良しとします。

PyDev開発元によると最新のEclipse 2021-03はクラッシュしているそうなので、その前の2020-12を使いました。なお2021-03は私の用途ではJython以外で問題なく使えています。

JREはJavaFXがOracle公式にて廃止される直前のJava8(調べると採用されたのは8のみ)、Jythonは最新1つ手前の2.7.1です。Jython 2.7.2はEclipseに導入できませんでした。

設定画像もアップしておきます。

最近はさしあたってコーディングしたいものがなく、ゲームをクリアする感覚で今回のような初心者向きの課題に取り組んでいます。

Javaを扱うようになって2000年代のブログ記事を読んだりしますが、扱っているツールやOS等のバージョン情報がほとんどなくて残念です。まさか2020年代に資料として読まれるとは思ってもいないのでしょう。

[Java] 60 Maven関連コマンド

[Java SE 13、maven.compiler : 1.7]

Maven関連コマンドをまとめました。

EclipseやVScode拡張機能で行き詰まったら、コマンドでのMavenプロジェクト作成をお勧めします。

1. 空のMavenプロジェクトを作成する。

# Mavenプロジェクトを作成するディレクトリに移る
cd /projects

# 空のMavenプロジェクトを作成する(対話モードではなく一括モード(BatchMode))
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false -DgroupId=[グループID(例 com.example)] -DartifactId=[プロジェクト名(例 test)]

2. プロジェクト完成後、依存関係にあるライブラリを含むjar実行ファイルを作成する。

# Mavenプロジェクトのディレクトリに移る
cd /projects/test

# jar実行ファイルを作成する
mvn assembly:assembly -DdescriptorId=jar-with-dependencies

3. Mainクラスとクラスパスの情報をjarのMANIFEST.MFに追加する。

# ターゲットディレクトリに移る
cd /projects/test/target

# Mainクラスとクラスパスの情報をjarのMANIFEST.MFに追加する
jar uvfm test-1.0-SNAPSHOT-jar-with-dependencies.jar manifest.txt

# オプションuvfmの意味
u:アップデート,v:詳細な標準出力生成,f:jarファイル名指定,m:manifestファイル名指定

# manifest.txtの内容
Main-Class: com.example.Main
Class-Path: com.example

4. 完成したjar実行ファイルをダブルクリックして起動し、動作確認します

ダブルクリックと同義のコマンドは以下の通りです(今回のプロジェクトの場合)。

javaの後にVScodeのlaunch.jsonで設定した引数、-cpオプション、そしてMainクラスを含むjarファイル名とMainクラスを入力します。

java --module-path /javafx-sdk-11.0.2/lib --add-modules javafx.controls,javafx.fxml -verbose:class -Dfile.encoding=UTF-8 -cp test-1.0-SNAPSHOT-jar-with-dependencies.jar com.example.Main

ネット情報等では”java -jar jar実行ファイル名”で起動できるとありますが、あくまでHello worldのような単純なプロジェクトの場合であって、それなりに手を入れたプロジェクトでは上のような長いコマンドになります。

[Java] 59 Mavenプロジェクトのjar実行ファイル作成

[Java SE 13、maven.compiler : 1.7]

EclipseやVScode拡張機能はターミナルのコマンド操作を代行してくれているブラックボックスであり、そこにバグが含まれていると真因が自分の操作なのかそれ以外なのか切り分けが難しくなります。またMavenが完璧だとも限りません。

そこでこれらのツールを使わずにコマンド操作だけでMavenプロジェクトを作成し、jar実行ファイルを作ってみました。

その結果、これまで上手くいかなかった原因が判明しました。POMにmaven-assembly-pluginについて記述しているにも関わらず、依存関係にあるライブラリを含んだjar実行ファイルを作成できていませんでした。

POMの該当箇所を削除し、以下コマンドでjar実行ファイルを作成しました。これで長いトンネルを抜けることができました。

mvn assembly:assembly -DdescriptorId=jar-with-dependencies

Eclipseについては自分の知識が足りないとしても、”mvn package”コマンドが正常に機能しないというのは想定外でした。まさかPOMに記述したことをそのまま実行してくれないとは。

まあターミナルへの出力をよく読めば分かることですし、そもそもファイル名にjar-with-dependenciesを含むjarファイルが作成されない時点で気付けよ、といった感じです。

やはりツールに頼らず、ログを丹念にチェックしながら自分でコマンドを打っていくのが王道ですね。

[Java] 58 JavaFX 16 Eclipseの設定

EclipseでのJavaFXプロジェクト(非Maven)作成について設定画像をアップします。

追々説明文は付けていく予定です。

これまで扱った感じではMavenプロジェクトは相当な難物という印象です。VScodeでもEclipseでも未だまともなjar実行ファイルを作れていないという体たらくです。

VScodeでMavenプロジェクトを作成しlaunchを確認できたら、あとはEclipseにインポートしてファイルをあらかじめ作成しておいたJavaFXプロジェクトに移すという手法でしばらくやってみようと考えています。Eclipseを扱うのは最小限に留めたいので。

[Java] 57 JavaFX 15 文字コード変換ツール その8 続バージョン検証

[macOS Catalina 10.15.7]

VScodeによる検証の続編です。

Googleによるuniversalchardetライブラリのリリース年が2008年というのを知り、古い環境でしか動かないのではないかと考えてJavaのバージョンをOracle版の8にして試してみたところ、いい線まで行きましたが結局NGでした。

さらにJavaSE6までさかのぼったものの上手くいかずで私のスキルではギブアップです。

しばらくはEclipse依存から脱することができないようです。

よくよく考えると現行のほとんどおよび過去のJDK(Oracle版とOpen版)は当然Intel Mac向けに作られたものなのでAppleシリコンMacにしてしまうとRosettaの呪縛から逃れられないのではないでしょうか。

そうだとすれば古いコード資産も活用したい私の場合はIntel Macを使い続けるのが得策のようですが、新しい技術も体感したいのでサブ機として次世代MシリーズMacを購入する方向に傾いています。

[Java] 56 JavaFX 14 文字コード変換ツール その7 universalchardet

[macOS Catalina 10.15.7]

VScodeでnkfコマンドを使った文字コード変換ツールはうまく作れましたが、universalchardetを使ったコードはjar実行ファイルにすると正常に動作しません。

macOSの影響かもしれないと考え、Mojave搭載機で試してみたものの結果はNGでした。

どうイジってもダメなので、EclipseでMavenではなく普通のJavaプロジェクトを作成してみるとあっさり成功しました。VScodeの軽量環境が気に入っていただけに残念です。

ところで未だにEclipseでまともなMavenプロジェクトを作れないでいます。今後の課題です。

自分の開発環境を確立するまではARMアーキテクチャのAppleシリコンMacへの移行はしない方がいいと考えています。リーク情報ではM1XやM2の開発状況がはかばかしくない、というのもありますが大丈夫なんでしょうか。

開発環境の脱RosettaやUSBポート等の拡張性に進歩がないのであれば、今年の購入は見送ってIntel Macを当面使い続けるつもりです。

package application;

import java.io.IOException;
import org.mozilla.universalchardet.UniversalDetector;

public class FileCharDetector {

    // コンストラクタ
    public FileCharDetector(){
    }

    public String main(String path) throws IOException {
        byte[] buf = new byte[4096];
        java.io.FileInputStream fis = new java.io.FileInputStream(path);

        UniversalDetector detector = new UniversalDetector(null);

        // 判定開始
        int nread;
        while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {
            detector.handleData(buf, 0, nread);
        }

        // 判定終了
        detector.dataEnd();

        // 判定結果取得
        final String detectedCharset = detector.getDetectedCharset();
 
        // 判定の初期化
        detector.reset();
        fis.close();

        return detectedCharset;
    }
}